通过略微重写以包括日志(log)记录,我们可以使这一点变得更清楚:
async function run(callback) {
let then = callback;
for (let i = 1; i <= 10; i++) {
console.log(callback === run ? "A" : "B", i);
then = await { then };
}
}
run(run);
.as-console-wrapper { max-height: 100% !important; }
这表明实际上有two个循环开始了.为了简单起见,我们称之为A和B.它们记录和await
,这意味着它们的日志(log)交错并导致A 1、B 1、A 2、B 2等.
这是因为第一个语句:run(run)
.它将相同的函数作为回调传递给自身.这不是call次回调,但这是解决这一问题的第一步.
理解正在发生的事情的下一步是await
.你可以await
任何值,如果most cases不是一个promise ,那也无关紧要.如果您有await 42;
,它只是假设值是Promise.resolve(42)
,并立即从下一个刻度继续操作.most个不promise 的情况也是如此.唯一的例外是thenables个对象,它们有.then()
个方法.
当等待thenable时,调用其then()
方法:
const thenable = {
then() {
console.log("called");
}
};
(async () => {
await thenable;
})()
这就解释了await { then }
的说法.它使用{ then: then }
的缩写,其中then
是传递给run
的回调.因此,它创建了一个thenable对象,当等待它时,它将执行回调.
这意味着第一次执行run()
,在循环A的第一次迭代中,代码实际上是await { then: run }
,它将再次执行run
,然后开始循环B.
值then
每次都会被覆盖,因此它只并行运行两个循环,而不是更多.
要完全掌握这段代码,还有更多相关信息.我之前展示了一个简单的方法,它只显示等待它调用该方法.然而,实际上await thenable
将使用两个参数调用.then()
——可以调用成功和失败的函数.就像Promise构造函数那样.
const badThenable = {
then() {
console.log("bad called");
}
};
(async () => {
await badThenable;
console.log("never reached");
})();
const goodThenable = {
then(resolve, reject) { //two callbacks
console.log("good called");
resolve(); //at least one needs to be called
}
};
(async () => {
await goodThenable;
console.log("correctly reached");
})();
这是相关的,因为run()
期望回调,并且当await { then: run }
执行时,它调用run(builtInResolveFunction)
,然后该run(builtInResolveFunction)
被传递到下一个await { then: builtInResolveFunction }
,该下一个await { then: builtInResolveFunction }
接着进行解析,从而导致A await
进行解析.
撇开所有这些不谈,交错日志(log)记录只是任务解析方式的一个因素:
(async () => {
for (let i = 1; i <= 10; i++){
console.log("A", i);
await Promise.resolve("just to force a minimal wait");
}
})();
(async () => {
for (let i = 1; i <= 10; i++) {
console.log("B", i);
await Promise.resolve("just to force a minimal wait");
}
})();
如果有两个异步函数正在运行,并且没有什么可等待的:
- 其中一个将运行到
await
分,然后将被暂停.
- 另一个将运行到
await
分,然后将被暂停.
- 重复1.和2.直到没有更多的等待.