Resolve Promises One After Another (I.E. in Sequence)

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.

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

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'));

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)?

resolving Promises sequentially

Get weird with Promises

An async queue, a spin-off one of my previous answers; I've added random completion time to simulate a real environment:

class Queue {

constructor() {

this.queue = [];

}

enqueue(obj) {

return this.queue.push(obj);

}

dequeue() {

return this.queue.shift();

}

hasWork() {

return (this.queue.length > 0);

}

}

class AsyncQueue extends Queue {

constructor(job) {

super();

this.job = job;

}

process(cb) {

return this.job(this.dequeue()).then(data => {

cb(data);

if (this.hasWork())

return this.process(cb);

});

}

}

//MUST RETURN Promise

function work() {

var duration = chooseDelay();

console.log('START JOB, I.E., MAKE REQUEST (will take %s)', duration);

return t_o(duration);

}

function report() {

console.log('JOB DONE');

}

function done() {

console.log('ALL WORK DONE');

}

function t_o(delay) {

return new Promise(function (resolve, reject) {

setTimeout(function () {

resolve();

}, delay);

});

}

function chooseDelay() {

var delays = [200, 1000, 4000, 100, 50, 7000];

return delays[parseInt(Math.random() * 100) % delays.length];

}

var q = new AsyncQueue(work);

for (var packet = 0; packet < 10; ++packet)

q.enqueue(packet);

q.process(report).then(done);

Run multiple Promise.all() in Sequence (Reading the second Promise.all only after the first one is completed)

Your 2-second timeout starts executing the moment you create its promise (*). Promise.all doesn't "start" the promises, it merely ensures they are all resolved (or one of them failed) before it will resolve.

In order to make the sequence you desire, you have to create promise6 after values1 has been received.

async function asyncCall() {
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'FirstBatch');
});
let values1 = await Promise.all([promise1, promise2, promise3]);
console.log(values1);

const promise4 = Promise.resolve(3);
const promise5 = 42;
const promise6 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'SecondBatch');
});
let values2 = await Promise.all([promise4, promise5, promise6]);
console.log(values2);
}

asyncCall();

Trouble with resolving promises in sequence

activityPlace is still just a promise you will need to await

console.log(await activityPlaces);

Note that you're not doing anything with the result of each promise (aside from the last one)

Wouldn't it be way easier to just throw this in a regular for loop and await one by one? the reduce pattern is useful for cases where you don't have async/await, but you don't seem to have this limitation:

const results = [];

for(const id of activityLocationIDs) {
results.push(await this.get(`places/${id}`));
}

console.log(result);

The above code matches the behavior of your first sample.

Resolve promises sequentially resulting in error

Reading through the sample code you posted, I made some assumptions.

  • First, from your use of ... promise.then(result => func().then ...
    I made the assumption that this.handleTransfer(payment) returns a
    function, and that function returns a promise.
  • Second, from your use
    of concatenation, I'm assuming the data your API call sends back is an
    array.

The promiseSerial implementation below returns a promise chain that resolves to a concatenation of all the arrays returned by the this.handleTransfer(payments) calls while preserving the sequence of the calls.

const promiseSerial = (funcs) => funcs.reduce((resultPromise, apiPromise) => {
return resultPromise.then((concatenatedAPIResponses) => {
const apiCallPromise = apiPromise(); //because this.handleTransfer(payment) returns a function
//const apiCallPromise = apiPromise; if this.handleTransfer(payment) returns a promise instead of a function

return apiCallPromise.then((apiCallResponse) => {
concatenatedAPIResponses = concatenatedAPIResponses.concat(apiCallResponse)
return concatenatedAPIResponses
})
})
}, Promise.resolve([]))

Codepen here



Related Topics



Leave a reply



Submit