Ethan Hermsey
Ethan Hermsey

Reputation: 940

Promise still blocking in FireFox, but not Chrome?

I made a loading manager that loads a few object classes. It loops the load function until everything is loaded. The items are promises that initializes classes (that take a long time to finish).

In chrome this works fine, the loading gif is animating and the textContent changes when loading different items. In firefox however, the gif isn't animating and I get this popup saying 'the website is slowing down the browser'. (the textContent does update though).

Am I doing this all wrong? Is there a better way?

Thanks in advance.

const loadManager = {
loading: false,
loadingItem: -1, //2
load(){
    if (!this.loading){
        //pick next item
        this.loadingItem++;

        //DONE!
        if(this.loadingItem >= this.objects.length){
            this.showPlayButton();
            return;
        }

        document.getElementById('loadingItem').textContent = this.objects[this.loadingItem].label;
        this.loading = true;

        //a timeout to give the DOM time to update the text in the 'loadingItem' element.
        setTimeout( ()=>{
            //start loading new object
            this.objects[this.loadingItem].load().then( this.loading = false );
        }, 200);
    }

    setTimeout(function(){this.load()}.bind(this), 2000);
},
showPlayButton(){

    if ( vehicles[0].object ){
        document.getElementById('loadingDiv').style.display = 'none';                
        document.getElementById('playButton').style.display = 'block';
    } else {
        console.log('assets not ready yet, trying again in 200ms');
        setTimeout(this.showPlayButton.bind(this), 200);
    }

},
objects:[
    {
        label: 'Earthlike planet',
        load(){
            return new Promise( resolve =>{
                let earthColors = [];
                earthColors = earthColors.concat( new THREE.Color('darkgreen').toArray() );
                earthColors = earthColors.concat( new THREE.Color('green').toArray() );
                earthColors = earthColors.concat( new THREE.Color('darkgrey').toArray() );
                earthColors = earthColors.concat( new THREE.Color('silver').toArray() );
                earthColors = earthColors.concat( new THREE.Color('gold').toArray() );
                earthColors = earthColors.concat( new THREE.Color('darkcyan').toArray() );
                celestialObjects.push(new Planet({
                    position: new THREE.Vector3(120000, 80000, 120000),                        
                    radius: 80000,
                    gravity: 12,
                    atmosphere: true,
                    colors: earthColors,
                    vegetation: true,
                    boulderTexture: new THREE.TextureLoader().load('./resources/boulderGrey.jpg'),
                    rocks: './resources/earthRocks.json',
                    grassTexture: './resources/grass.png'
                }));

                resolve();
            });
        }
    },
    {
        label: 'Orange planet',
        load(){
            return new Promise( resolve =>{
                let orangeColors = [];
                orangeColors = orangeColors.concat( new THREE.Color('darkorange').toArray() );
                orangeColors = orangeColors.concat( new THREE.Color('orange').toArray() );
                orangeColors = orangeColors.concat( new THREE.Color('darkred').toArray() );
                orangeColors = orangeColors.concat( new THREE.Color('silver').toArray() );
                orangeColors = orangeColors.concat( new THREE.Color('gold').toArray() );
                orangeColors = orangeColors.concat( new THREE.Color('darkcyan').toArray() );
                celestialObjects.push(new Planet({
                    position: new THREE.Vector3(- 240000, -200000, 150000),
                    radius: 40000,
                    gravity: 12,
                    atmosphere: true,
                    colors: orangeColors,
                    vegetation: false,
                    boulderTexture: new THREE.TextureLoader().load('./resources/boulderRed.jpg'),
                    rocks: './resources/redRocks.json',
                    grassTexture: './resources/grassRed.png'
                }));

                resolve();
            });
        }
    },
    {
        label: 'Asteroids',
        load(){
            return new Promise( resolve =>{
                for (let i = 0; i < 8; i ++){
                    celestialObjects.push( new Asteroid({
                        position: new THREE.Vector3(
                            random(-1, 1),
                            random(-1, 1),
                            random(-1, 1)
                        ).setLength( random( 80000, 300000)),
                        radius: random(3500, 8000),
                        gravity: 0,
                        atmosphere: false,
                        boulderTexture: new THREE.TextureLoader().load('./resources/boulderGrey.jpg'),
                    }))
                }

                resolve();
            });
        }
    }
]

}

Upvotes: 0

Views: 406

Answers (1)

miozzz
miozzz

Reputation: 113

I would do something using Promise.all and avoid the setTimeout and the recursive function unless you want to load them synchronously which would defeat the purpose using the Promises in the first place.

Using promises i would do something like this. Although this would load everything asynchronously.

load(){
if (!this.loading){
    //DONE!
    const psArr = []
    for(const obj in this.objects){
        const ps = obj.load()
        psArr.push(ps)
    }
    Promise.all(psArr).then(() => this.showPlayButton())
}

If you really want to keep the Promises but also want to keep the code synchronous you can do something like this.

load(){
if (!this.loading){
    //DONE!
    const psArr = [Promise.resolve()]
    let i = 0
    for(const obj in this.objects){
        const previous = psArr[i] 
        // first array element is a promise object that is already resolved
        previous.then(() => {
           //you can add your label changes inside this block.
           const ps = obj.load()
           psArr.push(ps)
        })
        // the obj.load() will now only run everytime the previous promise is resolved. 
        i++
    }
    // promise.all still works for this case
    Promise.all(psArr).then(() => this.showPlayButton())
}

this would also be way easier to write using async/await

Upvotes: 2

Related Questions