I am working on building Promise chain with setTimeouts in them. All of the Promises needs to be run in series not parallel. I am using Bluebird module to achieve serial flow of Promise execution.
Can someone please explain me why this code gives me output of 1,2,3,4 instead of 4,3,2,1?
var bluebirdPromise = require('bluebird'); function p1(value) { return new Promise(function(resolve, reject) { setTimeout(function(resolve) { console.log(value); resolve; }, value * 1000); }); } var arr = [p1(4), p1(3), p1(2), p1(1)]; bluebirdPromise.reduce(arr, function(item, index, length) { }).then(function (result) { });
There are several issues:
The console.log
you have is not made dependent on a previous resolved promise. There is just the time-out that determines when the output will happen. As you create all four promises at the "same" time, and thus all four setTimeout
calls are invoked simultaneously, their callbacks will be called at the determined time-out. It does not matter how you chain promises afterwards... To solve this, you need to move the console.log
in a then
callback, because that callback will only be executed when the previous promise in the chain has been resolved.
The resolve
function is not called in your code. You need to add parentheses.
The resolve parameter of the setTimeout
callback hides the real function with the same name: you need to remove that parameter.
Here is the suggested correction. For this snippet I have replaced the bluebird reduce
with a standard Array#reduce
, but it would work similarly with bluebird's reduce
:
function p1(value) { return new Promise(function(resolve, reject) { setTimeout(function() { // *** resolve(value); // *** }, value * 1000); }); } var arr = [p1(4), p1(3), p1(2), p1(1)]; arr.reduce(function(promise, next) { return promise.then(_ => next).then( value => { console.log(value); // *** return value; }); }, Promise.resolve());
If you have a promise-creator function, p
, and you want to run a sequence of promises in serial , there's no need for you to load an array with promises – instead, just let it be a normal array of values
Notice I'm not using value * 1000
here either – in your code, you thought you had to artificially choreograph the promises to fire in a particular order using calculated setTimeout delays; that's just not the case. Carefully observe the evaluation of the code below to see how we have a 1-second delay between each promise and .then
keeps things in order
Also note that this code will start outputting immediately after the first promise resolves – as opposed to waiting for all promises to resolve before outputting all values
const p = x => new Promise(f => setTimeout(f, 1e3, x)) const arr = [4,3,2,1] arr.reduce((acc, x) => acc.then(() => p(x)).then(console.log), Promise.resolve())
OK, so you have these promises running in serial order, but why? Unless later steps somehow depend on the result of earlier steps, there's no reason why you'd want to slow these down – ie, each promise's result is not dependent on the others, so just calculate them all as fast as possible. But you're worried that the order will be lost, right? Don't worry, everything will be OK – I'll even use a random delay to show you that the time each promise takes matters not
const p = x => new Promise(f => setTimeout(f, 1e3 * Math.random(), x)) const arr = [4,3,2,1] arr.map(p).reduce((acc, x) => acc.then(() => x).then(console.log), Promise.resolve())
So now, all of the promises can start immediately, and output will start as soon as the first promise is resolved (unlike Promise.all which would wait for all promises to finish before any values are available to you).
I only mention this as an alternative because the example you provided shows no real need for the promises to be executed in serial. You may have naively simplified your problem's domain, but only you know whether that's the case.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.