How to execute promises sequentially, passing the parameters from an array?
You can make the repeated application of .then
into a fold pretty neatly if you’re okay with creating as many promises as array elements as is the case in the question:
myArray.reduce(
(p, x) =>
p.then(() => myPromise(x)),
Promise.resolve()
)
but given support, an async function is a better choice. It’s nicely readable and has O(1) instead of O(n) memory overhead.
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
If you want to collect the return values as an array, that’s:
const mapSeries = async (iterable, fn) => {
const results = []
for (const x of iterable) {
results.push(await fn(x))
}
return results
}
or, without async function support,
const mapSeries = (iterable, fn) => {
const iterator = iterable[Symbol.iterator]()
const results = []
const go = () => {
const {value, done} = iterator.next()
if (done) {
return results
}
return fn(value).then(mapped => {
results.push(mapped)
return go()
})
}
return Promise.resolve().then(go)
}
Runnable snippet:
const myArray = [1, 2, 3, 4, 5, 6]
const sleep = ms =>
new Promise(res => {
setTimeout(res, ms)
})
const myPromise = num =>
sleep(500).then(() => {
console.log('done: ' + num)
})
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
.then(() => {
console.log('all done!')
})
Execute an Array of promises sequentially without using async/await
Here could be a possible option:
let p = Promise.resolve([]);
promisesArray.forEach(q => {
p = p.then(responses => {
//based on the nature of each q, to start execution
//use either q().then() or q.then()
return q().then(response => {
//Any further logic can be here.
console.log(response);
return responses.concat([response]);
})
})
})
p.then(responses => {
// here you have all of the responses.
})
How can I execute array of promises in sequential order?
If you already have them in an array then they are already executing. If you have a promise then it's already executing. This is not a concern of promises (I.E they are not like C# Task
s in that regard with .Start()
method). .all
doesn't execute anything
it just returns a promise.
If you have an array of promise returning functions:
var tasks = [fn1, fn2, fn3...];
tasks.reduce(function(cur, next) {
return cur.then(next);
}, RSVP.resolve()).then(function() {
//all executed
});
Or values:
var idsToDelete = [1,2,3];
idsToDelete.reduce(function(cur, next) {
return cur.then(function() {
return http.post("/delete.php?id=" + next);
});
}, RSVP.resolve()).then(function() {
//all executed
});
JavaScript Promises: Executing Promises Sequentially
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
Resolve promises one after another (i.e. in sequence)?
Update 2017: I would use an async function if the environment supports it:
async function readFiles(files) {
for(const file of files) {
await readFile(file);
}
};
If you'd like, you can defer reading the files until you need them using an async generator (if your environment supports it):
async function* readFiles(files) {
for(const file of files) {
yield await readFile(file);
}
};
Update: In second thought - I might use a for loop instead:
var readFiles = function(files) {
var p = Promise.resolve(); // Q() in q
files.forEach(file =>
p = p.then(() => readFile(file));
);
return p;
};
Or more compactly, with reduce:
var readFiles = function(files) {
return files.reduce((p, file) => {
return p.then(() => readFile(file));
}, Promise.resolve()); // initial
};
In other promise libraries (like when and Bluebird) you have utility methods for this.
For example, Bluebird would be:
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param
readAll.then(function(allFileContents){
// do stuff to read files.
});
Although there is really no reason not to use async await today.
How to execute promises in order and return all results
Try using an async function and iterating over the promises in a for...of loop:
Resolve promises one after another (i.e. in sequence)?
Execute promises map sequentially
The problem with your current code is that Promise.prototype.map
, like forEach
, does not wait for asynchronous functions called inside it to complete. (No asynchronous call will ever be waited for unless you tell the interpreter to do so explicitly with await
or .then
)
Have t1
await each call of t2
:
async function t1(){
let arr1 = [1,2,3,4,5];
const results = [];
for (const val of arr1) {
results.push(await t2(val));
}
return results;
}
Or if you want to use reduce
instead of async
/await
:
const delay = () => new Promise(res => setTimeout(res, 500));function t1(){ let arr1 = [1,2,3,4,5]; return arr1.reduce((lastProm, val) => lastProm.then( (resultArrSoFar) => t2(val) .then(result => [...resultArrSoFar, result]) ), Promise.resolve([]));}
function t2(event){ return delay().then(() => { console.log('iter'); return event; });}
t1() .then(results => console.log('end t1', results));
Sequential execution of Promise.all
the Promises unfortunatelly does not allow any control of their flow. It means -> once you create new Promise, it will be doing its asynchronous parts as they like.
The Promise.all
does not change it, its only purpose is that it checks all promises that you put into it and it is resolved once all of them are finished (or one of them fail).
To be able to create and control asynchronous flow, the easiest way is to wrap the creation of Promise into function and create some kind of factory method. Then instead of creating all promises upfront, you just create only one promise when you need it, wait until it is resolved and after it continue in same behaviour.
async function doAllSequentually(fnPromiseArr) { for (let i=0; i < fnPromiseArr.length; i++) { const val = await fnPromiseArr[i](); console.log(val); }}
function createFnPromise(val) { return () => new Promise(resolve => resolve(val));}
const arr = [];for (let j=0; j < 10; j++) { arr.push(createFnPromise(Math.random()));}
doAllSequentually(arr).then(() => console.log('finished'));
Related Topics
Protractor E2E Test Case for Downloading PDF File
No-JavaScript Detection Script + Redirect
Will Const and Let Make the Iife Pattern Unnecessary
Send a File as Multipart Through Xmlhttprequest
Resolve Circular References from JSON Object
How Might I Extract the Property Values of a JavaScript Object into an Array
Angular 2 Dependency Injection in Es5 and Es6
Error: Require() of Es Modules Is Not Supported When Importing Node-Fetch
Calling Member Function of Number Literal
Test If a Data Exist in Firebase
How to Control the Back Button Event in Jquery Mobile
JavaScript Read File Without Using Input
How to Call a C# Function from JavaScript
How to Access Constants in the Lib/Constants.Js File in Meteor
How to Implement Inheritance in Js Revealing Prototype Pattern