How to execute promises sequentially?

var promiseReturningFuncs = []; for(var i = 0; i < 5; i++){ promiseReturningFuncs.push(askQuestion); } var programmers = []; Promise.reduce(promiseReturningFuncs, function(resp, x) { console.log(typeof resp); if(typeof resp != "function") { programmers.push(resp); } return x(); }) .then(function(resp) { programmers.push(resp); console.log(programmers); }); 

My goal: execute the askQuestion function sequentially and allow an array of objects created by this function. (this function must be executed sequentially so that it can respond to user input)

So imagine the askQuestion function returns a promise that resolves the object I want to add to the array.

This is my dirty way to do it. I am looking to find a cleaner way to do this, ideally, I would not even need to click on an array, I would just have the latter. Then, where the answer is an array.

+2
source share
4 answers

Since you seem to be using the Bluebird promise library, you have a number of built-in options to organize the returned promise functions. You can use Promise.reduce() , Promise.map() with a concurrency value of 1, Promise.mapSeries or Promise.each() . If the iterator function returns a promise, they will all wait for the next iteration until that promise is resolved. What to use more depends on the mechanics of how your data is structured and what kind of result you want (neither what you actually show nor describe).

Suppose you have a set of functions that return a promise, and you want to name them one at a time, waiting for it to be resolved before calling the next one. If you want to get all the results, I would suggest Promise.mapSeries() :

 let arrayOfPromiseReturningFunctions = [...]; // call all the promise returning functions in the array, one at a time // wait for one to resolve before calling the next Promise.mapSeries(arrayOfPromiseReturningFunctions, function(fn) { return fn(); }).then(function(results) { // results is an array of resolved results from all the promises }).catch(function(err) { // process error here }); 

Promise.reduce() can also be used, but it will accumulate one result, passing it from one to another and ending with one final result (for example, Array.prototype.reduce() ).

Promise.map() is a more general version of Promise.mapSeries() that allows you to control the number of concurrency (the number of asynchronous operations in flight at the same time).

Promise.each() will also streamline your functions, but does not accumulate the result. It assumes that you either have no result, or you accumulate the result out of range or through side effects. I do not like to use Promise.each() because I do not like programming side effects.

+4
source

You can solve this in pure JS using ES6 features (ES2015):

 function processArray(arr, fn) { return arr.reduce( (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))), Promise.resolve([]) ); } 

It applies the function given to the array sequentially and resolves the array of results .

Using:

 const numbers = [0, 4, 20, 100]; const multiplyBy3 = (x) => new Promise(res => res(x * 3)); // Prints [ 0, 12, 60, 300 ] processArray(numbers, multiplyBy3).then(console.log); 

You want to double check browser compatibility, but this works on reasonably current versions of Chrome (v59), NodeJS (v8.1.2), and possibly most others.

+2
source

Executing one by one using a recursive function (not promising):

 (function iterate(i,result,callback){ if( i>5 ) callback(result);askQuestion().then(res=>iterate(i+1,result.concat([res]),callback); })(0,[],console.log); 

For shure, this can be wrapped in a promise:

 function askFive(){ return new Promise(function(callback){ (function iterate(i,result){ if( i>5 ) callback(result);askQuestion().then(res=>iterate(i+1,result.concat([res]),callback); })(0,[],console.log); }); } askFive().then(console.log); 

Or:

 function afteranother(i,promise){ return new Promise(function(resolve){ if(!i) return resolve([]); afteranother(i-1,promise).then(val=>promise().then(val2=>resolve(val.concat([val2]))); }); } afteranother(5,askQuestion).then(console.log); 
0
source

You can use recursion to advance to the next iteration in the then block.

 function promiseToExecuteAllInOrder(promiseReturningFunctions /* array of functions */) { var resolvedValues = []; return new Promise(function(resolve, reject) { function executeNextFunction() { var nextFunction = promiseReturningFunctions.pop(); if(nextFunction) { nextFunction().then(function(result) { resolvedValues.push(result); executeNextFunction(); }); } else { resolve(resolvedValues); } } executeNextFunction(); } } 
0
source

Source: https://habr.com/ru/post/1272467/


All Articles