Skip to content Skip to sidebar Skip to footer

Difference Between Run Sequence Promises By Creating A New Promise Then Run And Creating All Promises Then Run Each

Please help me explain why log result is different in 2 ways: Way 1: Log sequentially every 1 second Way 2: After 1 second all elements are logged. // Way 1 let sequence = Promise.

Solution 1:

You have sequenced your timers in "way 1" (they each fire a second apart) and run your timers in parallel in "way 2" so the timers all fire at about the same time. Here's the more detailed explanation:

In "way 1", you create a chain of promises with sequence = sequence.then() and the setTimeout() is called from within the sequence. So, timer 2 won't start until after timer 1 fires and so on. You will get each individual timer running truly sequential with each firing about 1 second apart.

In "way 2", you start all the timers at once in your .map(), so all the timers run in parallel, not in sequence. You are then trying to force them into a sequence with your sequence2 = sequence2.then() loop, but the async operations were already started in parallel so all this sequential loop doesn't really accomplish anything more than a Promise.all() would in terms of timing.

If you run each of these two snippets, they will log exactly when the timers fire and you can see the difference between the two.

Here, in a version of "way 1" that logs the time sequence of each timer, you can see the timers firing about 1 second apart:

// way 1
let startTime = Date.now();

function log() {
    let args = Array.from(arguments);
    // calc time since startTime
    let delta = (Date.now() - startTime) / 1000;
    args.unshift(delta + ": ");
    console.log.apply(console, args);
}

function delay(t, val) {
    return new Promise(resolve => {
        setTimeout(() => {
            log("timer fire");
            resolve(val);           
        }, t);
    });
}

// Way 1
let sequence = Promise.resolve();
[1,2,3,4].forEach((val)=> {
    sequence = sequence.then(()=> {
        return delay(1000, val);
    }).then(console.log);
});
sequence.then(() => {
    log("all done");
})

Here, in a version of "way 2" that logs the time sequence of each timer, you can see the timers firing at all about the same time:

// way 2
let startTime = Date.now();

function log() {
    let args = Array.from(arguments);
    // calc time since startTime
    let delta = (Date.now() - startTime) / 1000;
    args.unshift(delta + ": ");
    console.log.apply(console, args);
}

function delay(t, val) {
    return new Promise(resolve => {
        setTimeout(() => {
            log("timer fire");
            resolve(val);           
        }, t);
    });
}

//Way 2
let sequence2 = Promise.resolve();
[5,6,7,8].map(val => {
    return new delay(1000, val);
}).forEach(promise => {
    sequence2 = sequence2.then(() => promise).then(console.log);
});

sequence2.then(() => {
    log("all done");
});

When running each snippet, not in particular the time it takes to get to the "all done" message. The first one serializes four one second timers so it takes about 4 seconds to run. The second one runs all the timers in parallel so it takes about 1 second to run.


Additional explanation:

When you do this:

let arrayOfPromoises = [5,6,7,8].map(val => {
    return new Promise(resolve => setTimeout(resolve, 1000, val));
});

That code executes return new Promise(resolve => setTimeout(resolve, 1000, val)); four times in a row, one right after the other with no delays. Since each time that code is execute, it creates a new Promise object, you end up with an array of four promises.

Now, inside of that code you have this:

new Promise(resolve => setTimeout(resolve, 1000, val));

The Promise executor function (that's what the callback is called that you pass to the Promise constructor) is called IMMEDIATELY. There is no waiting. So, not only have you created four promises, but you've also started four times that are all running at once. These timers are already started. No amount of structuring with your sequence2 = sequence2.then() loop will change that. The timers are already running.


Solution 2:

To understand this you need to understand that a promise just wait's for something to resolve, so until a resolve is got the following (.then) code chain wont execute

For the first, way lets use different variables instead of the same sequence variable, then the code becomes.

var firstSequence = Promise.resolve()
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 1))})
                        .then(console.log);
var secondSequence = firstSequence
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 2))})
                        .then(console.log);
var thirdSequence = secondSequence
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 3))})
                        .then(console.log);
var foruthSequence = thirdSequence
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 4))})
                        .then(console.log);                       

from this you can see that the timeout calls do not happen before a promise is resolved. So they appear to be sequential and they keep getting pushed to wait for one second.

But the second form is equivalent to this.

function makePromise(val) {
    return new Promise(resolve => setTimeout(resolve, 1000, val)); 
}
var storageArray  = [makePromise(1), makePromise(2), makePromise(3), 
makePromise(4)];

we have already pushed all the setimout calls together in one go, so after one second all promises are already resolved. so (.then) wont need to wait wait.

 var firstSequence = Promise.resolve()
                    .then(()=> {return storageArray[0]})
                    .then(console.log);
 var secondSequence = firstSequence
                    .then(()=> {return storageArray[1]})
                    .then(console.log);
 var thirdSequence = secondSequence
                    .then(()=> {return storageArray[2]})
                    .then(console.log);
 var foruthSequence = thirdSequence
                    .then(()=> {return storageArray[3]})
                    .then(console.log); 

So even though they appear to be similar to the first syntax the promises are all, already resolved so no need to wait.


Post a Comment for "Difference Between Run Sequence Promises By Creating A New Promise Then Run And Creating All Promises Then Run Each"