Iterable Promises with evaluation for each promise - javascript

I have a list of items to be processed. Lets says A,B,C,D,E .. I have a list of promises which processes these items. Each promise can process 1 or more items. I have a list of items which need to be mandatorily processed.
Lets say A, C are mandatory items.
Promise 1 processes A,B
Promise 2 processes A,C
Promise 3 processes B,C.
I can return in any of the following cases
P1,P2 are completed (don't care about P3)
P1,P3 are completed (don't care about P2)
P2,P3 are completed (don't care about P1)
P1,P2,P3 are completed.
All promises (async calls) are started at the same item sequentially.
How do I handle this with Promise of iterables.?
One way I could think of is
Promise.race(
Promise.all(P1,P2),
Promise.all(P1,P3),
Promise.all(P2,P3),
Promise.all(P1,P2,P3)
)
This should work. But this requires me to construct the list of promise combinations based on the mandatoryItems and eachPromiseItems.
Is there a proper elegant way to handle this case in JavaScript?

As an optional advice. You can create a promise wrapper to process all of your promises and resolve it whenever you want.
function afterPromises(yourPromiseList, checker) {
var _P = new Promise((res, rej) => {
for (var p of yourPromiseList) {
p.then((data) => {
let checked = checker(data);
if(checked===true){
res()
}
if(checked===false){
rej()
}
})
}
})
return _P;
}
afterPromises([...yourPromiseList], () => {
var itemsDict = {}// a closure var to register which your items processed
return (data) => {
itemsDict[data.id]=true
//And your extra logic code
return true
//or return false
//or reutrn undefined
}
}).then(()=>{
//resolved
})
THE CODE NOT TESTED

I'm not sure that there is something built-in for this requirement but you can run over an array of promises and handle this logic like this:
const p1 = new Promise(resolve => {
setTimeout(() => {resolve(['A','B']);}, 500);
});
const p2 = new Promise(resolve => {
setTimeout(() => {resolve(['A','C']);}, 500);
});
const p3 = new Promise(resolve => {
setTimeout(() => {resolve(['B','C']);}, 500);
});
const mandatory = ['A','C'];
function promisesRunTillMandatoryCompleted(promises, mandatory) {
return new Promise((resolve, reject) => {
let results = [];
let completed = [];
promises.forEach((promise, index) => {
Promise.resolve(promise).then(result => {
results[index] = result;
completed = [...new Set(completed.concat(result))];
if (mandatory.every(elem=> completed.indexOf(elem) > -1)) {
resolve(results);
}
}).catch(err => reject(err));
});
});
}
promisesRunTillMandatoryCompleted([p1,p2,p3],mandatory)
.then(() => console.log('all mandatory commpleted'));

Related

Javascript resolve first element in array of promises that has been fullfilled in order

I'm doing multiple requests to different APIs (simulated by the delayedPromise function) which each take a certain amount of time to fulfill. If one of them fulfills I return the value it resolves and that's it. I'm currently doing those one after the other since the first request would be the most accurate while the second would be less accurate etc. So if the first request fails and the second fulfills I return the second
To speed things up I thought about parallelizing those requests. To accomplish that I'm creating an array that holds the promises and it awaits the result of the first element and if that fails it awaits the result of the second element etc...
Here's the code:
function promiseTest() {
return new Promise(function (resolve, reject) {
let promises = []
new Promise(async function (resolve, reject) {
promises.push(delayedPromise(true, 10000, "1")) //If true resolves after 10000 ms; false = reject
promises.push(delayedPromise(false, 1000, "2"))
promises.push(delayedPromise(false, 1000, "3"))
let answer;
let answer2
let answer3;
answer = await promises[0].catch(async err => {
answer2 = await promises[1].catch(async err => {
answer3 = await promises[2].catch(err => { reject(err) })
})
})
if (answer != undefined) {
resolve(answer)
}
else if (answer2 != undefined) {
resolve(answer2)
}
else if (answer3 != undefined) {
resolve(answer3)
}
}).then(res => resolve(res)).catch(err => {reject(err)})
})
}
function delayedPromise(bool, time,string ="") {
return new Promise(function (resolve, reject) {
if (bool) {
setTimeout(resolve(string), time)
}
else {
setTimeout(reject(), time)
}
})
}
The issue is that while this approach works it's
A: Most likely not the best solution and hardly scalable
and B: The exact code above is creating a bunch of uncaught exceptions errors. I believe this is when I receive a result for answer1 I'm not handling any of the rejections the other functions returned.
Is there any better way to do these kinds of things/ fix the above mentioned concerns?
I'd appreciate any ideas or improvements!
Many thanks in advance
PS: If you can phrase my title better please let me know I'm kind of struggling to convey my issue in one sentence
There is no builtin for this. Promise.any and Promise.allSettled are quite similar but do not do exactly what you want. You'll have to build it yourself indeed. This is what I would do:
async function first(iterable) {
const promises = [];
for (const thenable of iterable) {
const promise = Promise.resolve(thenable);
promises.push(promise);
promise.catch(() => {}); // ignore rejections, we'll handle them later
}
const errors = [];
for (const promise of promises) {
try {
return await promise;
} catch(error) {
errors.push(error);
}
}
throw new AggregateError(errors);
}
Code in the question simplifies to:
function promiseTest() {
let promises = [
delayedPromise(true, 10000, '1'),
delayedPromise(false, 1000, '2'),
delayedPromise(false, 1000, '3')
];
return promises[0]
.catch(() => promises[1])
.catch(() => promises[2]);
}
As soon as a successful promise is encountered, the chain will stop catching and the successful result will drop all the way through to be delivered (promise-wrpped) to promiseTest's caller.
The catch-chain could also be written as follows ...
Promise.reject()
.catch(() => promises[0])
.catch(() => promises[1])
.catch(() => promises[2]);
... which helps in formulating a proceduralized version for an array of any length, as follows:
function promiseTest() {
let promises = [...]; // array of Promises (any length)
return promises.reduce((p_, p) => p_.catch(() => p), Promise.reject());
}
Assuming that measures to avoid unhandled rejections are not taken in the formulation of the promises array, you can do so (with a series of "branch-catches") in the reduction ...
function promiseTest() {
let promises = [...]; // array of Promises (any length).
return promises.reduce((p_, p) => {
p.catch(() => null); // avoid unhandled rejections.
return p_.catch(() => p);
}, Promise.reject());
}
I think weighting the result to favor promises in order while still running them all in parallel (for best end-to-end time) will require building a special loop. I think you can do it like this:
const errResult = Symbol('errResult');
// fns is an array of functions to call
// args is a array of arguments to pass to each function (same args to each)
async function getResult(fns, args) {
// start all the async operations to run in parallel
const promises = fns.map(fn => {
// Rejections will resolve with a sentinel value so we can detect them
// This helps us avoid ever getting an unhandled rejection error
// on promises that reject after we've already found our desired value
return fn(...args).catch(err => {
return errResult;
});
});
// now await them in order so you can find the first
// one that completes (in order)
// Promises that rejected have been converted into an errResult
// resolve to help in avoiding unhandled rejections
// after we return with our good first result
for (let p of promises) {
let result = await p;
if (result !== errResult) {
return result;
}
}
throw new Error('no results');
}
// sample usage:
getResult([fn1, fn2, fn3, fn4], [argA,argB]).then(result => {
console.log(result);
}).catch(err => {
console.log("no results found");
});
If you want different args to each function, then just build that into fn1, fn2, etc... so they call the root function with their desired arguments and then let the args array be an empty array.

Javascript: Run async task in series(or sequence) without libraries

I want to run some asynchronous task in a loop, but it should execute in sequence order(one after another). It should be vanilla JS, not with any libraries.
var doSome = function(i) {
return Promise.resolve(setTimeout(() => {
console.log('done... ' + i)
}, 1000 * (i%3)));
}
var looper = function() {
var p = Promise.resolve();
[1,2,3].forEach((n) => {
p = p.then(() => doSome(n))
})
return p;
}
looper();
Current output:
calling for ...1
calling for ...2
calling for ...3
Promise {<resolved>: 8260}
done... 3
done... 1
done... 2
Expected output:
calling for ...1
calling for ...2
calling for ...3
Promise {<resolved>: 8260}
done... 1
done... 2
done... 3
Note: Kindly answer, if you tried and it's working as expected
So, from your comment below, I think your own example code isn't quite matching your description. I think what you want for your example is something closer to the below snippet:
var doSome = function(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
});
}
var looper = function() {
[1,2,3].forEach((n) => {
doSome(n).then(console.log);
});
}
looper();
Here, the array [1, 2, 3] is iterated over, and an asynchronous process is generated for each one. As each of those async processes complete, we .then on them and console log their resolved result.
So, now the question comes how to best queue the results? Below, I stored them into an array, then leveraged async/await in order to pause execution on the results until they complete in order.
// This is probably what you want
var doSome = function(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
});
}
var looper = async function() {
const nums = [1,2,3];
const promises = []
nums.forEach((n) => {
console.log(`Queueing ${n}`);
promises.push(doSome(n));
});
for (let promise of promises) {
const result = await promise;
console.log(result);
}
}
looper();
Now, we could have eliminated a loop and only executed one after the last completed:
// Don't use this-- it is less efficient
var doSome = function(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
});
}
var looper = async function() {
const nums = [1,2,3];
const promises = [];
for (let n of nums) {
console.log(`Queueing ${n}`);
const result = await doSome(n);
console.log(result);
};
}
looper();
But, as you can see in the log, this approach won't queue up the next async process until the previous one has completed. This is undesirable and doesn't match your use case. What we get from the two-looped approach preceding this one is that all async processes are immediately executed, but then we order/queue the results so they respect our predefined order, not the order in which they resolve.
UPDATE
Regarding Promise.all, async/await and the intended behavior of the queueing:
So, if you want to avoid using async/await, I think you could write some sort of utility:
var doSome = function(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
});
}
function handlePromiseQueue(queue) {
let promise = queue.shift();
promise.then((data) => {
console.log(data)
if (queue.length > 0) {
handlePromiseQueue(queue);
}
})
}
var looper = function() {
const nums = [1,2,3];
const promises = []
nums.forEach((n) => {
console.log(`Queueing ${n}`);
promises.push(doSome(n));
});
handlePromiseQueue(promises);
}
looper();
HOWEVER, let me be clear-- if user Bergi's assertion is correct, and it is not important that each async promise be executed upon as soon as it resolves, only that none of them be acted upon until they all have come back, then this can 100% be simplified with Promise.all:
// This is probably what you want
var doSome = function(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
});
}
function handlePromiseQueue(queue) {
let promise = queue.shift();
promise.then((data) => {
console.log(data)
if (queue.length > 0) {
handlePromiseQueue(queue);
}
})
}
var looper = function() {
const nums = [1,2,3];
const promises = []
nums.forEach((n) => {
console.log(`Queueing ${n}`);
promises.push(doSome(n));
});
Promise.all(promises).then(() => handlePromiseQueue(promises));
}
looper();
Finally, as Bergi also pointed out, I am playing fast and loose here by not setting up any catch on these various promises-- I omitted them for brevity in examples, but for your purposes you will want to include proper handling for errors or bad requests.

Add milliseconds delay to Array.map calls which returns Array of promises

My need is simple. I would like to delay calls to sendEmail by 100 milliseconds. The email service provider permits at most sending 10 emails per second.
Note, however, though .map is synchronous, it immediately returns a Promise.
I have tried setTimeout to no avail, such as setTimeout(() => resolve(x), 100) and setTimeout(() => {return new Promise....}, 100).
Thoughts?
const promises = userEmailArray.map((userEmail) => {
return new Promise((resolve, reject) => {
....
mailer.sendEmail(userEmail);
return resolve();
});
});
});
...
Promise.all(promises).then(() => resolve()).catch(error => reject(error));
There are a bunch of different ways to approach this. I'd probably just use a recursive chained promise myself and then you can more precisely use a timer based on the finish from the previous call and you can use promises for calling it and handling propagation of errors.
I've assumed here that your mailer.sendEmail() follows the node.js callback calling convention so we need to "promisify" it. If it already returns a promise, then you can use it directly instead of the sendEmail() function that I created.
Anyways, here are a bunch of different approaches.
Recall Same Function After Delay (delayed recursion)
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
function sendAll(array) {
let index = 0;
function next() {
if (index < array.length) {
return sendEmail(array[index++]).then(function() {
return delay(100).then(next);
});
}
}
return Promise.resolve().then(next);
}
// usage
sendAll(userEmailArray).then(() => {
// all done here
}).catch(err => {
// process error here
});
Use setInterval to Control Pace
You could also just use a setInterval to just launch a new request every 100ms until the array was empty:
// promisify
let sendEmail = util.promisify(mailer.sendEmail);
function sendAll(array) {
return new Promise((resolve, reject) => {
let index = 0;
let timer = setInterval(function() {
if (index < array.length) {
sendEmail(array[index++]).catch(() => {
clearInterval(timer);
reject();
});
} else {
clearInterval(timer);
resolve();
}
}, 100);
})
}
Use await to Pause Loop
And, you could use await in ES6 to "pause" the loop:
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
// assume this is inside an async function
for (let userEmail of userEmailArray) {
await sendEmail(userEmail).then(delay.bind(null, 100));
}
Use .reduce() with Promises to Sequence Access to Array
If you aren't trying to accumulate an array of results, but just want to sequence, then a canonical ways to do that is using a promise chain driven by .reduce():
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, data), t);
});
}
userEmailArray.reduce(function(p, userEmail) {
return p.then(() => {
return sendEmail(userEmail).then(delay.bind(null, 100));
});
}, Promise.resolve()).then(() => {
// all done here
}).catch(err => {
// process error here
});
Using Bluebird Features for both Concurrency Control and Delay
The Bluebird promise library has a couple useful features built in that help here:
const Promise = require('Bluebird');
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = Promise.promisify(mailer.sendEmail);
Promise.map(userEmailArray, userEmail => {
return sendEmail(userEmail).delay(100);
}, {concurrency: 1}).then(() => {
// all done here
}).catch(err => {
// process error here
});
Note the use of both the {concurrency: 1} feature to control how many requests are in-flight at the same time and the built-in .delay(100) promise method.
Create Sequence Helper That Can Be Used Generally
And, it might be useful to just create a little helper function for sequencing an array with a delay between iterations:
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve, t, data);
});
}
async function runSequence(array, delayT, fn) {
let results = [];
for (let item of array) {
let data = await fn(item);
results.push(data);
await delay(delayT);
}
return results;
}
Then, you can just use that helper whenever you need it:
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
runSequence(userEmailArray, 100, sendEmail).then(() => {
// all done here
}).catch(err => {
// process error here
});
You already have a 'queue' of sorts: a list of addresses to send to. All you really need to do now is pause before sending each one. However, you don't want to pause for the same length of time prior to each send. That'll result in a single pause of n ms, then a whole raft of messages being sent within a few ms of each other. Try running this and you'll see what I mean:
const userEmailArray = [ 'one', 'two', 'three' ]
const promises = userEmailArray.map(userEmail =>
new Promise(resolve =>
setTimeout(() => {
console.log(userEmail)
resolve()
}, 1000)
)
)
Promise.all(promises).then(() => console.log('done'))
Hopefully you saw a pause of about a second, then a bunch of messages appearing at once! Not really what we're after.
Ideally, you'd delegate this to a worker process in the background so as not to block. However, assuming you're not going to do that for now, one trick is to have each call delayed by a different amount of time. (Note that this does not solve the problem of multiple users trying to process large lists at once, which presumably is going to trigger the same API restrictions).
const userEmailArray = [ 'one', 'two', 'three' ]
const promises = userEmailArray.map((userEmail, i) =>
new Promise(resolve =>
setTimeout(() => {
console.log(userEmail)
resolve()
}, 1000 * userEmailArray.length - 1000 * i)
)
)
Promise.all(promises).then(() => console.log('done'))
Here, you should see each array element be processed in roughly staggered fashion. Again, this is not a scaleable solution but hopefully it demonstrates a bit about timing and promises.
It's simpler to just do an async iteration of the array.
function send(i, arr, cb) {
if (i >= arr.length) return cb();
mailer.sendEmail(arr[i]);
setTimeout(send, 100, i+1, arr, cb);
}
send(0, userEmailArray, function() { console.log("all done") });

Breaking a promise chain (with multiple happy flows and single chain)

This question has been asked before but I wasn't satisfied with solutions like rejecting and handling in catch for breaking for a happy flow. I want to know if we can end up with multiple happy flows in a linear promise chain without rejecting (except in case of errors).
Here's my code sample:
const goStraight = (steps) => {
return new Promise ((resolve, reject) => {
resolve(steps+2);
});
};
const turnLeft = (steps) => {
return new Promise ((resolve, reject) => {
resolve(++steps);
});
};
const turnRight = (steps) => {
return new Promise ((resolve, reject) => {
resolve(++steps);
});
};
goStraight(0)
.then((total_steps_taken) => {
if (total_steps_taken % 2 === 0)
return turnLeft(total_steps_taken);
else
return null; // This is where I want to break the chain
})
.then((total_steps_taken) => {
return turnRight(total_steps_taken);
}).then((total_steps_taken) => {
console.log("Finished with Total Steps : "+total_steps_taken);
})
In the case that I start with goStraight(1) the code would flow in the else condition and this is when I want to break the chain.
Just want to know if it can be done without creating branches or using promise rejection.
Other than using promise rejection, a possible approach to clean the code up could be to organize the happy paths into their own chains which get combined into a single chain.
In other words, every sequence of steps that should happen, well, sequentially, should be organized together.
In the particular snippet you provided, you always go right after you went left, so just make that an explicit chain that happens if the number of steps taken is even:
const happyPath = total_steps_taken => turnLeft(total_steps_taken)
.then(turnRight)
.then(logSteps);
goStraight(0).then(total_steps_taken => {
if (total_steps_taken % 2 === 0) {
return happyPath(total_steps_taken);
}
});
Full example:
const stepPromise = direction => x => {
console.log(`going ${x} steps ${direction}`);
return new Promise(resolve => {
setTimeout(() => resolve(x), 500);
});
}
const turnLeft = stepPromise('left')
const turnRight = stepPromise('right');
const goStraight = stepPromise('straight');
const logSteps = steps => console.log(`steps taken: ${steps}`);
const happyPath = steps => turnLeft(steps)
.then(turnRight)
.then(steps => console.log(`Took ${steps} steps`));
const branchOrQuit = steps => {
if (steps % 2 === 0) {
console.log('going down the happy path');
return happyPath(steps);
}
console.log('quitting');
}
const happyExample = () => {
console.log('\n --- EVEN ---');
return goStraight(2).then(branchOrQuit);
}
const sadExample = () => {
console.log('\n --- ODD ---');
return goStraight(1).then(branchOrQuit);
}
happyExample().then(sadExample);
If you don't want to continue at any point when total steps taken is odd then you can wrap all your functions in a checks if the total steps is not an odd number:
const checkNotOdd =
fn =>
total_steps_taken =>
(total_steps_taken%2===0)
? Promise.reject("no odd number of steps")
: fn(total_steps_taken)
checkNotOdd(goStraight)(0)
.then(
//not checking here, would otherwise reject
turnLeft
// checkNotOdd(turnLeft)
)
.then((total_steps_taken) =>
checkNotOdd(turnRight)
).then(
total_steps_taken => console.log("Finished with Total Steps : "+total_steps_taken),
err => console.warn("Failed with:",err)
)

How to make q.all execute in order like async.series [duplicate]

Consider the following code that reads an array of files in a serial/sequential manner. readFiles returns a promise, which is resolved only once all files have been read in sequence.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) => {
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start with the first file!
});
};
The above code works, but I don't like having to do recursion for things to occur sequentially. Is there a simpler way that this code can be re-written so that I don't have to use my weird readSequential function?
Originally I tried to use Promise.all, but that caused all of the readFile calls to happen concurrently, which is not what I want:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
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.
Here is how I prefer to run tasks in series.
function runSerial() {
var that = this;
// task1 is a function that returns a promise (and immediately starts executing)
// task2 is a function that returns a promise (and immediately starts executing)
return Promise.resolve()
.then(function() {
return that.task1();
})
.then(function() {
return that.task2();
})
.then(function() {
console.log(" ---- done ----");
});
}
What about cases with more tasks? Like, 10?
function runSerial(tasks) {
var result = Promise.resolve();
tasks.forEach(task => {
result = result.then(() => task());
});
return result;
}
This question is old, but we live in a world of ES6 and functional JavaScript, so let's see how we can improve.
Because promises execute immediately, we can't just create an array of promises, they would all fire off in parallel.
Instead, we need to create an array of functions that returns a promise. Each function will then be executed sequentially, which then starts the promise inside.
We can solve this a few ways, but my favorite way is to use reduce.
It gets a little tricky using reduce in combination with promises, so I have broken down the one liner into some smaller digestible bites below.
The essence of this function is to use reduce starting with an initial value of Promise.resolve([]), or a promise containing an empty array.
This promise will then be passed into the reduce method as promise. This is the key to chaining each promise together sequentially. The next promise to execute is func and when the then fires, the results are concatenated and that promise is then returned, executing the reduce cycle with the next promise function.
Once all promises have executed, the returned promise will contain an array of all the results of each promise.
ES6 Example (one liner)
/*
* serial executes Promises sequentially.
* #param {funcs} An array of funcs that return promises.
* #example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs =>
funcs.reduce((promise, func) =>
promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))
ES6 Example (broken down)
// broken down to for easier understanding
const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
* serial executes Promises sequentially.
* #param {funcs} An array of funcs that return promises.
* #example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))
Usage:
// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']
// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))
// execute them serially
serial(funcs)
.then(console.log.bind(console))
To do this simply in ES6:
function(files) {
// Create a new empty promise (don't do that with real people ;)
var sequence = Promise.resolve();
// Loop over each file, and add on a promise to the
// end of the 'sequence' promise.
files.forEach(file => {
// Chain one computation onto the sequence
sequence =
sequence
.then(() => performComputation(file))
.then(result => doSomething(result));
// Resolves for each file, one at a time.
})
// This will resolve after the entire chain is resolved
return sequence;
}
Addition example
const addTwo = async () => 2;
const addThree = async (inValue) => new Promise((resolve) => setTimeout(resolve(inValue + 3), 2000));
const addFour = (inValue) => new Promise((res) => setTimeout(res(inValue + 4), 1000));
const addFive = async (inValue) => inValue + 5;
// Function which handles promises from above
async function sequenceAddition() {
let sum = await [addTwo, addThree, addFour, addFive].reduce(
(promise, currPromise) => promise.then((val) => currPromise(val)),
Promise.resolve()
);
console.log('sum:', sum); // 2 + 3 + 4 + 5 = 14
}
// Run function. See console for result.
sequenceAddition();
General syntax to use reduce()
function sequence(tasks, fn) {
return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}
UPDATE
items-promise is a ready to use NPM package doing the same.
I've had to run a lot of sequential tasks and used these answers to forge a function that would take care of handling any sequential task...
function one_by_one(objects_array, iterator, callback) {
var start_promise = objects_array.reduce(function (prom, object) {
return prom.then(function () {
return iterator(object);
});
}, Promise.resolve()); // initial
if(callback){
start_promise.then(callback);
}else{
return start_promise;
}
}
The function takes 2 arguments + 1 optional. First argument is the array on which we will be working. The second argument is the task itself, a function that returns a promise, the next task will be started only when this promise resolves. The third argument is a callback to run when all tasks have been done. If no callback is passed, then the function returns the promise it created so we can handle the end.
Here's an example of usage:
var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
//return promise of async resizing with filename
};
one_by_one(filenames,resize_task );
Hope it saves someone some time...
With Async/Await (if you have the support of ES7)
function downloadFile(fileUrl) { ... } // This function return a Promise
async function main()
{
var filesList = [...];
for (const file of filesList) {
await downloadFile(file);
}
}
(you must use for loop, and not forEach because async/await has problems running in forEach loop)
Without Async/Await (using Promise)
function downloadFile(fileUrl) { ... } // This function return a Promise
function downloadRecursion(filesList, index)
{
index = index || 0;
if (index < filesList.length)
{
downloadFile(filesList[index]).then(function()
{
index++;
downloadRecursion(filesList, index); // self invocation - recursion!
});
}
else
{
return Promise.resolve();
}
}
function main()
{
var filesList = [...];
downloadRecursion(filesList);
}
My preferred solution:
function processArray(arr, fn) {
return arr.reduce(
(p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
Promise.resolve([])
);
}
It's not fundamentally different from others published here but:
Applies the function to items in series
Resolves to an array of results
Doesn't require async/await (support is still quite limited, circa 2017)
Uses arrow functions; nice and concise
Example usage:
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);
Tested on reasonable current Chrome (v59) and NodeJS (v8.1.2).
First, you need to understand that a promise is executed at the time of creation.
So for example if you have a code:
["a","b","c"].map(x => returnsPromise(x))
You need to change it to:
["a","b","c"].map(x => () => returnsPromise(x))
Then we need to sequentially chain promises:
["a", "b", "c"].map(x => () => returnsPromise(x))
.reduce(
(before, after) => before.then(_ => after()),
Promise.resolve()
)
executing after(), will make sure that promise is created (and executed) only when its time comes.
Nicest solution that I was able to figure out was with bluebird promises. You can just do Promise.resolve(files).each(fs.readFileAsync); which guarantees that promises are resolved sequentially in order.
With async/await of ES2016 (and maybe some features of ES2018), this can be reduced to this form:
function readFile(file) {
... // Returns a promise.
}
async function readFiles(files) {
for (file in files) {
await readFile(file)
}
}
I haven't seen another answer express that simplicity. The OP said parallel execution of readFile was not desired. However, with IO like this it really makes sense to not be blocking on a single file read, while keeping the loop execution synchronous (you don't want to do the next step until all files have been read). Since I just learned about this and am a bit excited about it, I'll share that approach of parallel asynchronous execution of readFile with overall synchronous execution of readFiles.
async function readFiles(files) {
await Promise.all(files.map(readFile))
}
Isn't that a thing of beauty?
This is a slight variation of another answer above. Using native Promises:
function inSequence(tasks) {
return tasks.reduce((p, task) => p.then(task), Promise.resolve())
}
Explanation
If you have these tasks [t1, t2, t3], then the above is equivalent to Promise.resolve().then(t1).then(t2).then(t3). It's the behavior of reduce.
How to use
First You need to construct a list of tasks! A task is a function that accepts no argument. If you need to pass arguments to your function, then use bind or other methods to create a task. For example:
var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)
I created this simple method on the Promise object:
Create and add a Promise.sequence method to the Promise object
Promise.sequence = function (chain) {
var results = [];
var entries = chain;
if (entries.entries) entries = entries.entries();
return new Promise(function (yes, no) {
var next = function () {
var entry = entries.next();
if(entry.done) yes(results);
else {
results.push(entry.value[1]().then(next, function() { no(results); } ));
}
};
next();
});
};
Usage:
var todo = [];
todo.push(firstPromise);
if (someCriterium) todo.push(optionalPromise);
todo.push(lastPromise);
// Invoking them
Promise.sequence(todo)
.then(function(results) {}, function(results) {});
The best thing about this extension to the Promise object, is that it is consistent with the style of promises. Promise.all and Promise.sequence is invoked the same way, but have different semantics.
Caution
Sequential running of promises is not usually a very good way to use promises. It's usually better to use Promise.all, and let the browser run the code as fast as possible. However, there are real use cases for it - for example when writing a mobile app using javascript.
My answer based on https://stackoverflow.com/a/31070150/7542429.
Promise.series = function series(arrayOfPromises) {
var results = [];
return arrayOfPromises.reduce(function(seriesPromise, promise) {
return seriesPromise.then(function() {
return promise
.then(function(result) {
results.push(result);
});
});
}, Promise.resolve())
.then(function() {
return results;
});
};
This solution returns the results as an array like Promise.all().
Usage:
Promise.series([array of promises])
.then(function(results) {
// do stuff with results here
});
Use Array.prototype.reduce, and remember to wrap your promises in a function otherwise they will already be running!
// array of Promise providers
const providers = [
function(){
return Promise.resolve(1);
},
function(){
return Promise.resolve(2);
},
function(){
return Promise.resolve(3);
}
]
const inSeries = function(providers){
const seed = Promise.resolve(null);
return providers.reduce(function(a,b){
return a.then(b);
}, seed);
};
nice and easy...
you should be able to re-use the same seed for performance, etc.
It's important to guard against empty arrays or arrays with only 1 element when using reduce, so this technique is your best bet:
const providers = [
function(v){
return Promise.resolve(v+1);
},
function(v){
return Promise.resolve(v+2);
},
function(v){
return Promise.resolve(v+3);
}
]
const inSeries = function(providers, initialVal){
if(providers.length < 1){
return Promise.resolve(null)
}
return providers.reduce((a,b) => a.then(b), providers.shift()(initialVal));
};
and then call it like:
inSeries(providers, 1).then(v => {
console.log(v); // 7
});
Using modern ES:
const series = async (tasks) => {
const results = [];
for (const task of tasks) {
const result = await task;
results.push(result);
}
return results;
};
//...
const readFiles = await series(files.map(readFile));
Most of the answers dont include the results of ALL promises individually, so in case someone is looking for this particular behaviour, this is a possible solution using recursion.
It follows the style of Promise.all:
Returns the array of results in the .then() callback.
If some promise fails, its returned immediately in the .catch() callback.
const promiseEach = (arrayOfTasks) => {
let results = []
return new Promise((resolve, reject) => {
const resolveNext = (arrayOfTasks) => {
// If all tasks are already resolved, return the final array of results
if (arrayOfTasks.length === 0) return resolve(results)
// Extract first promise and solve it
const first = arrayOfTasks.shift()
first().then((res) => {
results.push(res)
resolveNext(arrayOfTasks)
}).catch((err) => {
reject(err)
})
}
resolveNext(arrayOfTasks)
})
}
// Lets try it 😎
const promise = (time, shouldThrowError) => new Promise((resolve, reject) => {
const timeInMs = time * 1000
setTimeout(()=>{
console.log(`Waited ${time} secs`)
if (shouldThrowError) reject(new Error('Promise failed'))
resolve(time)
}, timeInMs)
})
const tasks = [() => promise(1), () => promise(2)]
promiseEach(tasks)
.then((res) => {
console.log(res) // [1, 2]
})
// Oops some promise failed
.catch((error) => {
console.log(error)
})
Note about the tasks array declaration:
In this case is not possible to use the following notation like Promise.all would use:
const tasks = [promise(1), promise(2)]
And we have to use:
const tasks = [() => promise(1), () => promise(2)]
The reason is that JavaScript starts executing the promise immediatelly after its declared. If we use methods like Promise.all, it just checks that the state of all of them is fulfilled or rejected, but doesnt start the exection itself. Using () => promise() we stop the execution until its called.
You can use this function that gets promiseFactories List:
function executeSequentially(promiseFactories) {
var result = Promise.resolve();
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory);
});
return result;
}
Promise Factory is just simple function that returns a Promise:
function myPromiseFactory() {
return somethingThatCreatesAPromise();
}
It works because a promise factory doesn't create the promise until it's asked to. It works the same way as a then function – in fact, it's the same thing!
You don't want to operate over an array of promises at all. Per the Promise spec, as soon as a promise is created, it begins executing. So what you really want is an array of promise factories...
If you want to learn more on Promises, you should check this link:
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
If you want you can use reduce to make a sequential promise, for example:
[2,3,4,5,6,7,8,9].reduce((promises, page) => {
return promises.then((page) => {
console.log(page);
return Promise.resolve(page+1);
});
}, Promise.resolve(1));
it'll always works in sequential.
I really liked #joelnet's answer, but to me, that style of coding is a little bit tough to digest, so I spent a couple of days trying to figure out how I would express the same solution in a more readable manner and this is my take, just with a different syntax and some comments.
// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']
// next convert each item to a function that returns a promise
const functions = urls.map((url) => {
// For every url we return a new function
return () => {
return new Promise((resolve) => {
// random wait in milliseconds
const randomWait = parseInt((Math.random() * 1000),10)
console.log('waiting to resolve in ms', randomWait)
setTimeout(()=>resolve({randomWait, url}),randomWait)
})
}
})
const promiseReduce = (acc, next) => {
// we wait for the accumulator to resolve it's promise
return acc.then((accResult) => {
// and then we return a new promise that will become
// the new value for the accumulator
return next().then((nextResult) => {
// that eventually will resolve to a new array containing
// the value of the two promises
return accResult.concat(nextResult)
})
})
};
// the accumulator will always be a promise that resolves to an array
const accumulator = Promise.resolve([])
// we call reduce with the reduce function and the accumulator initial value
functions.reduce(promiseReduce, accumulator)
.then((result) => {
// let's display the final value here
console.log('=== The final result ===')
console.log(result)
})
As Bergi noticed, I think the best and clear solution is use BlueBird.each, code below:
const BlueBird = require('bluebird');
BlueBird.each(files, fs.readFileAsync);
I find myself coming back to this question many times and the answers aren't exactly giving me what I need, so putting this here for anyone that needs this too.
The code below does sequential promises execution (one after another), and each round consists of multiple callings:
async function sequence(list, cb) {
const result = [];
await list.reduce(async (promise, item) => promise
.then(() => cb(item))
.then((res) => result.push(res)
), Promise.resolve());
return result;
}
Showcase:
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
<script src="https://unpkg.com/#babel/standalone#7/babel.min.js"></script>
<script type="text/babel">
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function readFile(url, index) {
console.log('Running index: ', index);
// First action
const firstTime = await axios.get(url);
console.log('First API response: ', firstTime.data.activity);
// Second action
await sleep(1000);
// Third action
const secondTime = await axios.get(url);
console.log('Second API response: ', secondTime.data.activity);
// Fourth action
await sleep(1000);
return secondTime.data;
}
async function sequence(urls, fn) {
const result = [];
await urls.reduce(async (promise, url, index) => promise.then(() => fn(url, index)).then((res) => result.push(res)), Promise.resolve());
return result;
}
const urls = [
'https://www.boredapi.com/api/activity',
'https://www.boredapi.com/api/activity',
'https://www.boredapi.com/api/activity',
];
(async function init() {
const result = await sequence(urls, readFile);
console.log('result', result);
})()
</script>
I use the following code to extend the Promise object. It handles rejection of the promises and returns an array of results
Code
/*
Runs tasks in sequence and resolves a promise upon finish
tasks: an array of functions that return a promise upon call.
parameters: an array of arrays corresponding to the parameters to be passed on each function call.
context: Object to use as context to call each function. (The 'this' keyword that may be used inside the function definition)
*/
Promise.sequence = function(tasks, parameters = [], context = null) {
return new Promise((resolve, reject)=>{
var nextTask = tasks.splice(0,1)[0].apply(context, parameters[0]); //Dequeue and call the first task
var output = new Array(tasks.length + 1);
var errorFlag = false;
tasks.forEach((task, index) => {
nextTask = nextTask.then(r => {
output[index] = r;
return task.apply(context, parameters[index+1]);
}, e=>{
output[index] = e;
errorFlag = true;
return task.apply(context, parameters[index+1]);
});
});
// Last task
nextTask.then(r=>{
output[output.length - 1] = r;
if (errorFlag) reject(output); else resolve(output);
})
.catch(e=>{
output[output.length - 1] = e;
reject(output);
});
});
};
Example
function functionThatReturnsAPromise(n) {
return new Promise((resolve, reject)=>{
//Emulating real life delays, like a web request
setTimeout(()=>{
resolve(n);
}, 1000);
});
}
var arrayOfArguments = [['a'],['b'],['c'],['d']];
var arrayOfFunctions = (new Array(4)).fill(functionThatReturnsAPromise);
Promise.sequence(arrayOfFunctions, arrayOfArguments)
.then(console.log)
.catch(console.error);
Your approach is not bad, but it does have two issues: it swallows errors and it employs the Explicit Promise Construction Antipattern.
You can solve both of these issues, and make the code cleaner, while still employing the same general strategy:
var Q = require("q");
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
var readSequential = function(index) {
if (index < files.length) {
return readFile(files[index]).then(function() {
return readSequential(index + 1);
});
}
};
// using Promise.resolve() here in case files.length is 0
return Promise.resolve(readSequential(0)); // Start!
};
This is my sequentially implementation that I use in various projects:
const file = [file1, file2, file3];
const fileContents = sequentially(readFile, files);
// somewhere else in the code:
export const sequentially = async <T, P>(
toPromise: (element: T) => Promise<P>,
elements: T[]
): Promise<P[]> => {
const results: P[] = [];
await elements.reduce(async (sequence, element) => {
await sequence;
results.push(await toPromise(element));
}, Promise.resolve());
return results;
};
Here is my Angular/TypeScript approach, using RxJS:
Given an array of URL strings, convert it into an Observable using the from function.
Use pipe to wrap the Ajax request, immediate response logic, any desired delay, and error handling.
Inside of the pipe, use concatMap to serialize the requests. Otherwise, using Javascript forEach or map would make the requests at the same time.
Use RxJS ajax to make the call, and also to add any desired delay after each call returns.
Working example: https://stackblitz.com/edit/rxjs-bnrkix?file=index.ts
The code looks like this (I left in some extras so you can choose what to keep or discard):
import { ajax } from 'rxjs/ajax';
import { catchError, concatMap, delay, from, of, map, Observable } from 'rxjs';
const urls = [
'https://randomuser.me/api/',
'https://randomuser.me/api/',
'https://randomuser.me/api/',
];
const delayAfterCall = 500;
from(urls)
.pipe(
concatMap((url: string) => {
return ajax.getJSON(url).pipe(
map((response) => {
console.log('Done! Received:', response);
return response;
}),
catchError((error) => {
console.error('Error: ', error);
return of(error);
}),
delay(delayAfterCall)
);
})
)
.subscribe((response) => {
console.log('received email:', response.results[0].email);
});
On the basis of the question's title, "Resolve promises one after another (i.e. in sequence)?", we might understand that the OP is more interested in the sequential handling of promises on settlement than sequential calls per se.
This answer is offered :
to demonstrate that sequential calls are not necessary for sequential handling of responses.
to expose viable alternative patterns to this page's visitors - including the OP if he is still interested over a year later.
despite the OP's assertion that he does not want to make calls concurrently, which may genuinely be the case but equally may be an assumption based on the desire for sequential handling of responses as the title implies.
If concurrent calls are genuinely not wanted then see Benjamin Gruenbaum's answer which covers sequential calls (etc) comprehensively.
If however, you are interested (for improved performance) in patterns which allow concurrent calls followed by sequential handling of responses, then please read on.
It's tempting to think you have to use Promise.all(arr.map(fn)).then(fn) (as I have done many times) or a Promise lib's fancy sugar (notably Bluebird's), however (with credit to this article) an arr.map(fn).reduce(fn) pattern will do the job, with the advantages that it :
works with any promise lib - even pre-compliant versions of jQuery - only .then() is used.
affords the flexibility to skip-over-error or stop-on-error, whichever you want with a one line mod.
Here it is, written for Q.
var readFiles = function(files) {
return files.map(readFile) //Make calls in parallel.
.reduce(function(sequence, filePromise) {
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
Note: only that one fragment, Q(), is specific to Q. For jQuery you need to ensure that readFile() returns a jQuery promise. With A+ libs, foreign promises will be assimilated.
The key here is the reduction's sequence promise, which sequences the handling of the readFile promises but not their creation.
And once you have absorbed that, it's maybe slightly mind-blowing when you realise that the .map() stage isn't actually necessary! The whole job, parallel calls plus serial handling in the correct order, can be achieved with reduce() alone, plus the added advantage of further flexibility to :
convert from parallel async calls to serial async calls by simply moving one line - potentially useful during development.
Here it is, for Q again.
var readFiles = function(files) {
return files.reduce(function(sequence, f) {
var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
That's the basic pattern. If you wanted also to deliver data (eg the files or some transform of them) to the caller, you would need a mild variant.
If someone else needs a guaranteed way of STRICTLY sequential way of resolving Promises when performing CRUD operations you also can use the following code as a basis.
As long as you add 'return' before calling each function, describing a Promise, and use this example as a basis the next .then() function call will CONSISTENTLY start after the completion of the previous one:
getRidOfOlderShoutsPromise = () => {
return readShoutsPromise('BEFORE')
.then(() => {
return deleteOlderShoutsPromise();
})
.then(() => {
return readShoutsPromise('AFTER')
})
.catch(err => console.log(err.message));
}
deleteOlderShoutsPromise = () => {
return new Promise ( (resolve, reject) => {
console.log("in deleteOlderShouts");
let d = new Date();
let TwoMinuteAgo = d - 1000 * 90 ;
All_Shouts.deleteMany({ dateTime: {$lt: TwoMinuteAgo}}, function(err) {
if (err) reject();
console.log("DELETED OLDs at "+d);
resolve();
});
});
}
readShoutsPromise = (tex) => {
return new Promise( (resolve, reject) => {
console.log("in readShoutsPromise -"+tex);
All_Shouts
.find({})
.sort([['dateTime', 'ascending']])
.exec(function (err, data){
if (err) reject();
let d = new Date();
console.log("shouts "+tex+" delete PROMISE = "+data.length +"; date ="+d);
resolve(data);
});
});
}
Array push and pop method can be used for sequence of promises. You can also push new promises when you need additional data. This is the code, I will use in React Infinite loader to load sequence of pages.
var promises = [Promise.resolve()];
function methodThatReturnsAPromise(page) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Resolve-${page}! ${new Date()} `);
resolve();
}, 1000);
});
}
function pushPromise(page) {
promises.push(promises.pop().then(function () {
return methodThatReturnsAPromise(page)
}));
}
pushPromise(1);
pushPromise(2);
pushPromise(3);
(function() {
function sleep(ms) {
return new Promise(function(resolve) {
setTimeout(function() {
return resolve();
}, ms);
});
}
function serial(arr, index, results) {
if (index == arr.length) {
return Promise.resolve(results);
}
return new Promise(function(resolve, reject) {
if (!index) {
index = 0;
results = [];
}
return arr[index]()
.then(function(d) {
return resolve(d);
})
.catch(function(err) {
return reject(err);
});
})
.then(function(result) {
console.log("here");
results.push(result);
return serial(arr, index + 1, results);
})
.catch(function(err) {
throw err;
});
}
const a = [5000, 5000, 5000];
serial(a.map(x => () => sleep(x)));
})();
Here the key is how you call the sleep function. You need to pass an array of functions which itself returns a promise instead of an array of promises.

Categories