简体   繁体   English

具有setTimeout的JavaScript串行承诺

[英]JavaScript Serial Promises with setTimeout

I am working on building Promise chain with setTimeouts in them. 我正在使用setTimeouts构建Promise链。 All of the Promises needs to be run in series not parallel. 所有的Promise都需要串联而不是并行运行。 I am using Bluebird module to achieve serial flow of Promise execution. 我正在使用Bluebird模块来实现Promise执行的串行流程。

Can someone please explain me why this code gives me output of 1,2,3,4 instead of 4,3,2,1? 有人可以解释一下为什么此代码给我输出1,2,3,4而不是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. 您拥有的console.log并不依赖于先前解决的承诺。 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. 当您在“相同”时间创建所有四个Promise,因此同时调用了所有四个setTimeout调用时,它们的回调在确定的超时时被调用。 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. 紧随其后如何链接承诺都没有关系...要解决此问题,您需要将console.log移动到then回调中,因为该回调仅在链中的先前承诺已解决时才执行。

  • The resolve function is not called in your code. 您的代码中未调用resolve函数。 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. setTimeout回调的resolve参数隐藏具有相同名称的函数:您需要删除该参数。

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 : 对于此代码段,我已用标准Array#reduce替换了bluebird的reduce ,但它与bluebird的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 如果您有一个Promise-Creator函数p ,并且要以串行方式运行一个Promise 序列 ,则无需加载一个带有Promise的数组-而是让它成为一个普通的值数组

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; 注意,我这里也不使用value * 1000 –在您的代码中,您认为必须使用计算得出的setTimeout延迟人为地编排要按特定顺序触发的承诺。 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 仔细观察代码的评价下面来看看我们是如何让每个承诺之间有1秒钟的延迟和.then让事情为了

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 还要注意,此代码将在第一个promise解析后立即开始输出–而不是在输出所有值之前等待所有promise解析

 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). 因此,现在,所有的Promise都可以立即启动,并且第一个Promise解析后即开始输出(与Promise.all不同,Promise.all会等待所有Promise完成,然后再提供任何值)。

I only mention this as an alternative because the example you provided shows no real need for the promises to be executed in serial. 我只提到这是一种替代方法,因为您提供的示例表明并没有真正需要按顺序执行promise。 You may have naively simplified your problem's domain, but only you know whether that's the case. 您可能天真地简化了问题的范围,但只有您知道情况是否如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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