首页 > 解决方案 > 在循环内使用 Promise

问题描述

我想了解这段代码是如何工作的。我已经承诺工作了一段时间,但我想知道这里发生了什么。

for(let i=0 ; i< 3 ;i++){
   new Promise((resolve,reject)=>{
   console.log("i = "+i)
   resolve(i)
  })
  .then(r=>{
   console.log("promise 1  of "+r)
   return new Promise((res,rej)=>res(r))
  })
  .then(r=>{
    console.log("promise 2  of "+r)
    return new Promise((res,rej)=>res(r))
  })
}

console.log("finish")

输出是

i = 0 
i = 1 
i = 2 
finish 
promise 1  of 0 
promise 1  of 1 
promise 1  of 2 
promise 2  of 0 
promise 2  of 1 
promise 2  of 2

为什么在其他承诺执行之前完成显示。

提前致谢

标签: javascriptpromise

解决方案


为什么在其他承诺执行之前完成显示。

因为该代码中没有任何内容告诉它console.log在运行之前等待承诺完成。如果您想这样做,请将它们收集在一个数组中,然后使用:

Promise.all(theArray)
    .then(() => {
        console.log("finish");
    });

...告诉它等待他们所有人。

请记住,then处理程序始终是异步调用的。所以你的代码创建了三个 Promise 链,然后console.log在最后输出,然后获取then处理程序的异步回调——所以这些结果随后会显示出来。


旁注:您问题中的代码可以更简单地写成这样:

for(let i = 0; i < 3; i++) {
    console.log("i = " + i);
    Promise.resolve(i)
    .then(r => {
        console.log("promise 1  of " + r);
        return r;
    })
    .then(r => {
        console.log("promise 2  of " + r);
        return r;
    });
}

console.log("finish");

为什么:

  • Promise的executor 函数console.log("i = " + i);(传递给构造函数的函数)是同步运行的,因此将行作为 executor 函数的第一行,或者就在您创建 Promise 的行的上方没有区别。
  • Promise.resolve使用您提供的值创建一个已解决的承诺。
  • then 总是创造一个新的承诺。当您从处理程序返回非承诺值时then,该值是承诺then返回的分辨率值。如果你返回一个 Promise,那么then创建的 Promise 将“奴隶”于你返回的 Promise(等待它解决或拒绝,然后做同样的事情)。

在评论中,您提出了一个非常聪明的问题:

如果“then”被异步调用,那么这些 Promise 的输出顺序可能无法预测,对吧?

小点:then不是异步调用的,它的处理程序(你传递给它的函数)是异步调用的。

在一般情况下,是的,没错,您无法预测独立承诺(未链接在一起的承诺)的顺序。您可以预测链接在一起Promise(链中较早的 Promise 在后面的 Promise 之前处理)。在您的代码中,您有三个独立的链(一个用于 i = 0,一个用于 i = 1,另一个用于 i = 2);每个链都有两个附加的处理程序(“1 of”和“2 of”)。所以你知道链“i = 0”中的“1 of”将在同一个链中的“2 of”之前发生,但在一般情况下,你不知道那个i = 0是第一个还是第i = 2一个或其他。

但是,在这种特定情况下,可以预测顺序(至少在浏览器中,也可以在 Node.js 上),因为每个链中的初始承诺都是预先解析的,这意味着对其then处理程序的调用放在微任务中呼叫时立即排队¹ then。微任务队列在每个任务(又名“宏任务”)结束时运行。因此,运行主代码的任务会立即将回调按顺序排列到每个链的“1 of”处理程序。当该处理程序运行时,它将一个微任务排队以调用“2 of”处理程序。所以自从承诺开始解决,我们知道我们会得到你得到的输出。但是在一般情况下,您是正确的,您无法在链之间进行预测。

¹这是它的网络术语;JS 规范将其称为 PromiseJobs 队列。有关此答案中的“微任务”和任务/宏任务的更多信息。


推荐阅读