4

In an attempt to understand promises more clearly, i have been reading up a few very interesting articles on the same. I came across the following code which works perfectly for executing promises sequentially. But i am not able to understand how it works.

 function doFirstThing(){ return new Promise(function(resolve,reject){ setTimeout(()=>{ resolve(1); },1000) }) } function doSecondThing(res){ return new Promise(function(resolve,reject){ setTimeout(()=>{ resolve(res + 1); },1000) }) } function doThirdThing(res){ return new Promise(function(resolve,reject){ setTimeout(()=>{ resolve(res + 2); },1000) }) } promiseFactories = [doFirstThing, doSecondThing, doThirdThing]; function executeSequentially(promiseFactories) { var result = Promise.resolve(); // this is the most problematic line promiseFactories.forEach(function (promiseFactory) { result = result.then(promiseFactory);// what is happening here ? }); return result; } executeSequentially(promiseFactories) 

I do understand that promises are executed as soon as they are created. For some reason i am not able to understand the flow of execution. Especially this following line:

var result = Promise.resolve()//and empty promise is created. 

Please if somebody can help me understand how calling the promiseFactory method inside the 'then' method of the empty promise makes it execute sequentially, like so. Or is it because of the forEach loop ?

result = result.then(promiseFactory); 

I tried replacing the 'forEach' with a 'map' function and still yielded the same result. i.e, the methods where executed sequentially. Also, how is the value passed from one chained function to other ?

Any help or article/blog is highly appreciated.

4 Answers 4

3

The executeSequentially method returns all the Promises one after each other. It happens to iterate over promiseFactory, but it could be written as:

function executeSequentially(promiseFactories) { return doFirstThing() .then(() => doSecondThing()) .then(doThirdThing() ); } 

It is just the same. We are basically returning a Promise.

Now, however, we want to iterate over a collection of promises.

When iterating, we need to attach the current Promise to the previous with a then. But the forEach does not expose the next Promise -or the previous- in every iteration. And yet we still need it in order to keep chaining Promises one by one. Hence, the result 'hack':

function executeSequentially(promiseFactories) { var result = Promise.resolve(); /*We need a thing that keeps yelling the previous promise in every iteration, so we can keep chaining. This 'result' var is that thing. This is keeping a Promise in every iteration that resolves when all the previous promises resolve sequentially. Since we don't have a Promise in the array previous to the first one, we fabricate one out of 'thin air' with Promise.resolve() */ promiseFactories.forEach(function (promiseFactory) { result = result.then(promiseFactory); /* Here result is update with a new Promise, with is the result of chaining `result` with the current one. Since `result` already had all the previous ones, at the end, `result` will be a Promise that depends upon all the Promises resolution.*/ }); return result; } 

Now, there's also a syntax quirk that maybe is puzzling you:

result = result.then(promiseFactory); 

This line is pretty much the same as the following:

result = result.then(resolvedValue => promiseFactory(resolvedValue)); 

Please if somebody can help me understand how calling the promiseFactory method inside the 'then' method of the empty promise makes it execute sequentially, like so. Or is it because of the forEach loop ?

First thing first, promiseFactory is a pretty bad name there. The method should be better written as follows:

function executeSequentially(promises) { var result = Promise.resolve(); // this is the most problematic line promises.forEach(function (currentPromise) { result = result.then(currentPromise);// what is happening here ? }); return result; } 

So:

how calling the currentPromise method inside the 'then' method of the empty promise makes it execute sequentially?

It makes execute sequentially because when you attach a Promise to another by then, it executes sequentially. Is a then thing, it is not at all related to the fact that we are iterating over Promises. With plain Promises outside an iteration it works pretty much the same:

Promise.resolve() // fake Promises that resolves instanly .then(fetchUsersFromDatabase) // a function that returns a Promise and takes // like 1 second. It won't be called until the first one resolves .then(processUsersData) // another function that takes input from the first, and // do a lot of complex and asynchronous computations with data from the previous promise. // it won't be called until `fetchUsersFromDatabase()` resolves, that's what // `then()` does. .then(sendDataToClient); // another function that will never be called until // `processUsersData()` resolves 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the answer. Now i clearly understand the code snippet.
Thanks for the answer. Now i still confused.
3

You can image a Promise as a box with execution inside. As far as the promise is created, the execution starts. To get the result value, you have to open the box. You can use then for it:

Promise.resolve(5).then(result => console.log(result)); // prints 5 

If you want to chain promises you can do it by opening the box one by one:

Promise.resolve(5) .then(result => Promise.resolve(result + 1)) .then(result => Promise.resolve(result * 2)) .then(result => console.log(result)); // prints 12 

This chaining makes the executions synchronous (one by one).

If you want to execute several promises asynchronously (you don't chain results), you can use Promise.all:

Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]) .then(result => console.log(result)); // prints [1,2,3] 

In your case:

Promise.all(promiseFactories).then(result => console.log(result)); 

Another option how to work with promises is to await them:

(async ()=> { var res1 = await Promise.resolve(5); var res2 = await Promise.resolve(res1 + 1); var res3 = await Promise.resolve(res2 * 2); console.log(res3); // prints 12 })(); 

await works similar to then - it makes asynchronous execution to synchronous.

In your case:

async function executeSequentially(promiseFactories) { for (const p of promiseFactories) { const result = await p; console.log(result); } } 

Note: await packs a value into a Promise out of the box:

var res1 = await 5; // same as await Promise.resolve(5) 

4 Comments

thanks for the response. So is it safe to understand that executing promises inside a loop using await ensure they are executed sequentially ?
Yes, definitely. await resolves promises synchronously.
Also,@ttulka do you agree that promise.all() always executes in parallel ? BTW thanks for the neat and simple explanation :-)
Yes, Promise.all awaits all the promises asynchronously and then returns the resolved results in an array ordered in the same order as given as parameter.
2

It is always recommended to use Promise.all if you want such behaviour:

function doFirstThing() { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(1); }, 1000) }) } function doSecondThing(res) { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(res + 1); }, 1000) }) } function doThirdThing(res) { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(res + 2); }, 1000) }) } let promiseFactories = [doFirstThing(2), doSecondThing(1), doThirdThing(3)]; Promise.all(promiseFactories) .then(data => { console.log("completed all promises", data); })

To run it sequentially one after another:

function doFirstThing() { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(1); }, 1000) }) } function doSecondThing(res) { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(res + 1); }, 3000) }) } function doThirdThing(res) { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(res + 2); }, 5000) }) } promiseFactories = [doFirstThing, doSecondThing, doThirdThing]; function executeSequentially(promiseFactories) { promiseFactories.forEach(function(promiseFactory) { promiseFactory(1).then((data) => { console.log(data) }); }); } executeSequentially(promiseFactories);

6 Comments

Promise.all() runs the methods in parallel not sequentially.
@NidhinRaj added the code for that. Check the updated answer
Thanks. I am looking for an explanation on how it is running sequentially. And why var result = Promise.resolve(); is outside the loop. Why does Promise.resolve().then(promiseFactory) in side the loop not work? Also, try setting the timeout value as 1000 for all factories. You will find that all the promises are run parallel.
@NidhinRaj we do not need var result = Promise.resolve();
@NidhinRaj if timeout will be 1 sec then it will initialize those timeouts to 1 sec and all the promise is resolved at the same time in 1 sec. So to make those look sequentially I have changed the timeout value
|
1

If we lay out the foreach loop it will look like the following

function doFirstThing(){ return new Promise(function(resolve,reject){ setTimeout(()=>{ console.log(1); resolve(1); },1000) }) } function doSecondThing(res){ return new Promise(function(resolve,reject){ setTimeout(()=>{ console.log(2); resolve(res + 1); },2000) }) } function doThirdThing(res){ return new Promise(function(resolve,reject){ setTimeout(()=>{ console.log(3); resolve(res + 2); },3000) }) } Promise.resolve() .then(doFirstThing()) .then(doSecondThing()) .then(doThirdThing());

I do understand that promises are executed as soon as they are created. For some reason i am not able to understand the flow of execution. Especially this following line: var result = Promise.resolve()//and empty promise is created.

This is just to get hold of the promise chain's starting point. Here it is an already resolved promise. To better understand it you can use one of your promises to get hold of the promise chain like below.

let promiseFactories= [doSecondThing, doThirdThing]; let result = doFirstThing(); promiseFactories.forEach(function (promiseFactory) { result = result.then(promiseFactory); }); 

This will also work.

2 Comments

Thanks for the answer. This was the point i was trying to understand. With regards to your code, if we set all the timeout to 1000, it seems like all promises are run simultaneously. They can be run in sequential order only if they are in a loop. Am i correct ?
Correct, because the loop creates a dynamic promise chain that will run sequentially.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.