【发布时间】:2021-08-30 13:05:38
【问题描述】:
这是一个简单的异步函数:
async function f(){
let data = await (new Promise(r => setTimeout(r, 1000, 200)));
console.log(data)
return data + 100;
}
f()
console.log('me first');
以下是该计划中将要发生的事情:
- f() 将被调用
- 它进入 f 执行上下文并运行 await 表达式 - 由于函数调用的优先级高于 await 关键字,我们的新 Promise() 函数将首先运行,该函数将立即返回一个待处理的 Promise,然后等待关键字将起作用.
- 我们设置了一个计时器,它将在 1 秒后解决我们的承诺
- 由于尚未解决,await 将返回一个待处理的 Promise,我们会从当前执行上下文中跳出一层,这恰好是我们开始执行全局代码的全局执行上下文。
- 我们点击“console.log('me first')”行并记录“me first”
- 现在 1 秒后,我们的 promise 已经解决,这意味着我们的 await 表达式后面的所有代码都将被放入一个容器中,并放入微任务队列中,以便在下一次执行时执行! (在底层它可能会调用 .next() 方法,就像生成器函数一样,但我们不要深入探讨)
- 我们的同步代码已完成,我们的微任务队列中有一个任务,我们将其放入调用堆栈以运行!
- 它运行,一旦我们回到我们的异步函数,它就会从它离开的地方开始,即等待表达式。
- 由于我们的 promise 现在已解决,我们的 await 关键字将从已解决的 promise 中提取值并将其分配给变量 d。
- 我们将该值记录到下一行。
- 最后,在最后一行,我们从 async 函数返回值 + 100,这意味着我们之前返回的待处理承诺(如第 4 行中所述)使用该值解决,即 300!
如您所见,此时我对异步等待非常满意! 现在这是我们等待迭代的代码!
async function f(){
let p1 = new Promise(function (r){
setTimeout(r, 2000, 1)
})
let p2 = new Promise(function (r){
setTimeout(r, 1000, 2)
})
let p3 = new Promise(function (r){
setTimeout(r, 500, 3)
})
let aop = [p1, p2, p3];
for await (let elem of aop ){
let data = await elem;
console.log(data)
}
}
f()
console.log('global');
现在直观地说,假设 await 的行为与我们之前的 async 函数中的行为相同,您会期望这种情况发生!
- f() 被调用
- 我们创建了三个 Promise p1、p2、p3
- p1 将在 2 秒后解析,p2 在 1 秒后解析,p3 在 500 m/s 后解析
- 我们将这些 Promise 放在一个名为 aop 的数组中(因为 for 可以使用具有 Symbol.iterator 作为属性的对象,并且所有数组都具有该属性。)
- 现在这就是我觉得棘手的地方!
- 我们将遍历每个元素,对于第一个元素,我们进入循环并立即等待我们的第一个元素,即我们的第一个承诺 p1。
- 由于它还没有解决,它会像我们之前的示例一样返回一个待处理的承诺,或者是吗?
- 对于其他两个 Promise,p2 和 p3 也会发生同样的情况,返回更多两个待处理的 Promise。
- 我们的异步函数完成了,我们在全局执行中再往外一层,打印出 global:console.log('global');
- 此时,我们的 p3 承诺已经解决,因为它应该只需要 500 毫秒才能完成,所以它首先解决了!接着!也许在那之后我们留下的任何代码(console.log(data))都会被放入微任务队列以供以后执行?
- 按照这个逻辑,它应该打印 3,因为我们的最后一个承诺首先解决,然后是 2,然后是 1,但它打印 1、2、3! 那么我在写作中遗漏了哪些关键点? 因为 await 显然是并行的,所以它会遍历每个元素而不等待第一个元素完成,那么为什么它会这样工作?
【问题讨论】:
-
您说的是“500ms”,但您的代码说的是“5000”毫秒。
-
@StriplingWarrior - 糟糕,我刚刚编辑了!感谢您指出。
-
5 年前当我们做出决定时,我在找到一个讨论中说“人们会对这种行为感到惊讶”后,我只是大声笑了起来:D
-
@imheretolearn1 不,
[p1, p2, p3]不是按其解析顺序排列的 promise 流,它是一个数组,将按顺序迭代。for循环不会跳过元素或任意重新排序它们 - 并且for await循环在前进之前等待每个迭代器步骤。不,这与阻塞代码无关,它仍然是异步的 - 注意await关键字。
标签: javascript asynchronous async-await promise scheduled-tasks