Waiting for gltf loader callback to end

Hi,
I have a glb model that is being loaded using gltf loader inside a function similar to this:

load() {
   this.gltfLoader.load('model.glb', (data) => {
      this.object = data.scene
      
      const geometry = this.extractAndMergeOuterMesh(this.object);
      const material = new MeshPhongMaterial({
        color: 0x666666,
        opacity: 0,
        transparent: true,
      });

      this.mesh = new Mesh(geometry, material);
   }
}

I am calling this load function and some other functions from inside another function like this:

originalFunc () {
   this.preload()
   this.load()
   this.postLoad1()
   this.postload2()
}

postLoad1() and postLoad2() use the mesh from load() function, but the problem is that the model load takes so long that before the mesh is created inside the load() function, postLoad1() and postLoad(2) get called and so I get errors. I tried calling postLoad1() and postLoad(2) after a like 2-second delay and it works on one of my laptops that is fast and powerful, but on the other one, I need to increase the delay to like 5 seconds. I don’t think that’s how I am supposed to do it properly so that I don’t have to introduce a random delay/wait time.

I have also tried using promises and async-await, but nothing works.

Any suggestions or help would be much appreciated.

I think you need to use promises in async functions and set await. (setTimeOut is not need it)

for example here are a little piece of my current proyect, hope help you understand what I mean:

  //load model
  async load_Model(){

    this.vars.helper = new MMDAnimationHelper({sync:false, resetPhysicsOnLoop:true, afterglow:0.0});

    await this.vars.loader.load(this.vars.modelFile, async ( mmd )=> 
    {
      const promise = new Promise<void>(async (resolve, reject) => {
    
      this.vars.model=mmd;     

      this.vars.helper.add(this.vars.model, {
        animation: this.vars.model.animations,
        physics: true,
        weight:1
      } );
      


      //-------------LOAD HUMAN ANIMATIONS
      for(let i=0; i<this.vars.vmdFiles.length;i++)
      {
        await this.loadVMD(i);
        this.vars.loading_porcent=Math.round(i*100/this.vars.vmdFiles.length);
      }

              resolve();
              
      
              }); 
    
    
  
              promise.then(async () => {
                await this.animacion.loadOrbitControl();
     
               }).then( async ()=>{ await this.animacion.loadMixerModel()})
               .then( async ()=>{ await this.loadObjects(); this.vars.loading_porcent=100;})
               .then( ()=>{ this.animate(); })
  
      
    },
    this.vars.onProgress,
    this.vars.onError
    );
  }
1 Like

Timeouts are dangerous in such cases!
Slow connection would need longer timeouts… so better use callbacks.

You could put your postLoad functions into a callback that fires after the model is loaded like this:

load( callback ) {
   this.gltfLoader.load('model.glb', (data) => {
      this.object = data.scene
      
      const geometry = this.extractAndMergeOuterMesh(this.object);
      const material = new MeshPhongMaterial({
        color: 0x666666,
        opacity: 0,
        transparent: true,
      });

      this.mesh = new Mesh(geometry, material);
      callback();
   }
}

originalFunc () {
   this.preload()
   this.load( function() {
      this.postLoad1()
      this.postload2()
   })

}

1 Like

Yes, the 1st contact with async file loading can be quite entertaining :sunglasses: Been through that just recently myself. Here’s how I solved this, it’s similar to a semaphore:

dataArray = [];
dataArray_ready = false;
...
...
const loader = new THREE.FileLoader();
loader.setResponseType( "arraybuffer" );

loader.load(
	// resource URL
	filename,

	function ( data ) {
		// process data and fill dataArray[] which I set up as global
		fillDataArray( data );
 
		},

		// onProgress callback
		function ( xhr ) {
//			console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
		},

		// onError callback
		function ( err ) {
			console.error( 'An error happened' );
		}

	);

}

Then, in your animation loop, periodically check whether dataArray.length has become non-zero:

   function render() {

	if(( dataArray.length != 0 ) && !dataArray_ready ) {
		...
		// if so, you may safely call postLoad() stuff here
		...
		dataArray_ready = true;
	} else {
		// automatically consume frames until `dataArray`  is ready.
		// After that, update on OrbitControl() 'change' events only.
		if ( !dataArray_ready ) requestAnimationFrame( render );
	}
	renderer.render( scene, camera );
	
   }

1 Like

This seems to be the most straightforward solution. I did test callback but in the wrong places.
Thanks a lot. I wish I had asked this earlier. Wasted way too much time.

You’re welcome.
Nested callbacks are used a lot, as long as you are not entering the callback hell…harhar

I suggest try to avoid use Callback (because callback hell). Callbacks was the way people do it. The new way is async/await. And in my personal experience is the simplest way to do it.

I´m sorry. Maybe my code was no friendly (I written it 2 years ago). here are another example. if you want to learn a little more about async/await you can click here

async originalFunc(){
   await this.preload();
   await this.load();
   await this.postLoad1();
   await this.postload2();
}

preload(){
  //do something
  return;
}
load(){
  //do something
  return;
}
postLoad1(){
  //do something
  return;
}
postload2(){
  //do something
  return;
}

1 Like