Why nested promises are not working as expected?

can someone try to fix my problem?

I am trying to get this output:

100x do C
done B
done A

But everytime i get this one:

10x do C
done B
done A

Is it possible to get it without using async/await feature?

function a() {
    let myPromise = new Promise(function(myResolve, myReject) {
        b();
        myResolve()
    });

    myPromise.then(function() {
        console.log('done A')
    });
}

function b() {
    let myPromise = new Promise(function(myResolve, myReject) {
        for (i = 0; i < 10; i++) {
            c();
        }
    myResolve()
    });
    
    myPromise.then(function() {
    console.log('done B')
    });
}

function c() {
    for (i = 0; i < 10; i++) {
      console.log('do C')
    }
}

a()

1 thought on “Why nested promises are not working as expected?”

  1. The problem is that you’re not declaring i, so you’re creating an implicit global variable that both b and c write to. (I wrote an article about these years ago: The Horror of Implicit Globals.)

    In the below, I’m going to make it three runs of three instead of ten runs of ten, just so we can see it more clearly (and so I don’t have to increase the size of the in-snippet console’s history — it has an option for that, but I don’t recall the details).

    I strongly recommend using strict mode so that this is the error it always should have been. Here’s your code in strict mode and with rejection handlers added:

    "use strict";
    
    function a() {
        let myPromise = new Promise(function(myResolve, myReject) {
            b();
            myResolve()
        });
    
        myPromise.then(function() {
            console.log('done A')
        }).catch(function(err) {
            console.error(err);
        });
    }
    
    function b() {
        let myPromise = new Promise(function(myResolve, myReject) {
            for (i = 0; i < 3; i++) {
                c();
            }
            myResolve()
        });
        
        myPromise.then(function() {
            console.log('done B')
        }).catch(function(err) {
            console.error(err);
        });
    }
    
    function c() {
        for (i = 0; i < 3; i++) {
          console.log('do C')
        }
    }
    
    a()

    If you declare i (in both places), you’ll get the result you expect.

    function a() {
        let myPromise = new Promise(function(myResolve, myReject) {
            b();
            myResolve()
        });
    
        myPromise.then(function() {
            console.log('done A')
        });
    }
    
    function b() {
        let myPromise = new Promise(function(myResolve, myReject) {
            for (let i = 0; i < 3; i++) { // ***
                c();
            }
            myResolve()
        });
        
        myPromise.then(function() {
            console.log('done B')
        });
    }
    
    function c() {
        for (let i = 0; i < 3; i++) { // ***
          console.log('do C')
        }
    }
    
    a()

    but, beware that the reason you’re getting the output you’re getting is that everything except the calls to the fulfillment handlers is synchronous. If instead of a for loop you were doing some asynchronous process, there’s nothing preventing you from getting done B or done A before all of the work done by c is done, because there’s nothing connecting those promises together.

    Let’s see that. Suppose we have an asynchronous version of console.log that waits 10ms before writing the log entry (but we’ll use console.log to write out done A and done B immediately, so you can see when they ran):

    function asyncLog(...msgs) {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(...msgs);
                resolve();
            }, 10);
        });
    }
    

    Let’s look at what happens:

    function asyncLog(...msgs) {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(...msgs);
                resolve();
            }, 10);
        });
    }
    
    function a() {
        let myPromise = new Promise(function(myResolve, myReject) {
            b();
            myResolve();
        });
    
        myPromise.then(function() {
            console.log('done A');
        });
    }
    
    function b() {
        let myPromise = new Promise(function(myResolve, myReject) {
            for (let i = 0; i < 3; i++) {
                c();
            }
            myResolve()
        });
        
        myPromise.then(function() {
            console.log('done B')
        });
    }
    
    function c() {
        for (let i = 0; i < 3; i++) {
          asyncLog('do C');
        }
    }
    
    a();

    Notice how things aren’t connected to each other.

    To connect them, you’d have to make b wait for c‘s promise to be fulfilled, and make a wait for b‘s promise to be fulfilled. Something like this:

    function asyncLog(...msgs) {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(...msgs);
                resolve();
            }, 10);
        });
    }
    
    function a() {
        let myPromise = new Promise((resolve, reject) => {
            resolve(b());
        });
    
        return myPromise
        .then(() => {
            console.log('done A');
        });
    }
    
    function b() {
        let myPromise = Promise.resolve();
        for (let i = 0; i < 3; i++) {
            myPromise = myPromise.then(c);
        }
        
        return myPromise
        .then(() => {
            console.log('done B')
        });
    }
    
    function c() {
        let p = Promise.resolve();
        for (let i = 0; i < 3; i++) {
            p = p.then(() => asyncLog('do C'));
        }
        return p;
    }
    
    a()
    .then(() => {
        console.log("all done");
    })
    .catch(error => {
        console.error(`Error: ${error.message}`);
    });
    Reply

Leave a Comment