javascript - 在循环内使用 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
为什么在其他承诺执行之前完成显示。
提前致谢
解决方案
为什么在其他承诺执行之前完成显示。
因为该代码中没有任何内容告诉它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 队列。有关此答案中的“微任务”和任务/宏任务的更多信息。
推荐阅读
- python - 从 Django 中的数据集中获取数据
- mongodb - MongoDB 使用 $lookup、$unwind、$match 进行搜索
- arrays - 如何处理这个组合学和博弈论问题?
- react-native - 如何使用天才聊天和 Firebase 身份验证实现双向聊天功能?
- flutter - 我的容器的颜色属性不起作用,它没有给出任何错误 Flutter
- javascript - 如何在 javascriot 中创建soap php
- python-3.x - 使用 render_template() 时 Gunicorn 缓存 Flask Jinja2 模板
- ios - 使用 URLSession downloadTask 在设备上下载数据后如何获取/打印数据?
- sql-server - SQL Server:存储过程不工作,但在存储过程之外工作
- c - 编译C程序的麻烦