简体   繁体   中英

JavaScript Serial Promises with setTimeout

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM