[英]Correct mental model for a Javascript async function's 'await': generator's 'yield' vs. 'promise.then()'?
生成器的产量与promise.then()中的哪个是更*正确的心理模型,可以理解“等待”?
属性比较,通过使用调试器逐步浏览以下代码段来推断:
等待:
await不会暂停/暂停正在运行的异步函数的执行。 (正在运行的异步函数“运行至完成”,在解释器达到第一个等待状态时返回未决的诺言。然后立即将其从调用堆栈中删除。)
等待等待诺言实现。
await expression
将函数的其余代码包装在微任务中。
发电机收益:
yield promise
确实可以确保在执行剩余代码之前已经promise
。 promise.then(回调):
//promise returning function function foo(whoCalled) { let p = new Promise(function(resolve, reject) { setTimeout( () => { console.log('resolving from setTimeout - called by: ' + whoCalled) resolve('resolve value') }, .1) }) return p } //async await async function asyncFunc() { await foo('async function') //rest of running function's code… console.log('async function howdy') } //generator yield: function* gen() { yield foo('generator function') //rest of running function's code… console.log('generator function howdy') } //promise.then(): function thenFunc() { let r = foo('promise.then function').then(() => { //rest of running function's code… console.log('promise.then() howdy') }) return r } //main function main() { //async await var a = asyncFunc() console.log(a) //logs Promise { <pending> } //the rest of the code following await foo() runs as a microtask runs once foo() resolves. The call stack was cleared. //generator var g = gen() console.log(g) // logs Object [Generator] {} var p = g.next().value console.log(p) //logs Promise { <pending> } g.next() //the rest of the code following yield running gen function's code runs. call stack was not cleared. //promise.then() var x = thenFunc() console.log(x) //logs Promise { <pending> } //the then(callback) microtask runs once foo() resolves. The call stack was cleared } main() console.log('main is off the call stack - launch/startup macrotask completing. Event loop entering timer phase.')
而且,超越这种比较,究竟是什么样的精确思维模型await
引擎盖下呢?
等待最新的ECMAScript规范以供参考: https : //www.ecma-international.org/ecma-262/10.0/index.html#await
在V8源代码中等待: https : //github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/builtins/builtins-builtins-async-function-gen.cc#L252
这不是一个。 实际上,它们都是在一起: async
/ await
= yield
+ then
+一个跑步者。
async function
的确会被await
关键字挂起,就像生成器function*
会被yield
关键字挂起一样。 在控制流语句中间停止和恢复执行的机制完全相同。
不同之处在于这些延续的驱动方式以及函数返回的内容。 发电机函数创建一个名为当发电机对象,你必须明确地调用next()
来自外部的方法来运行代码yield
的yield
。 另一方面,异步函数会创建一个Promise,并自行管理执行。 它不等待外部next()
调用,而是尽快运行每个异步步骤。 它不会将这些next()
调用返回的结果值返回Promise.resolve()
到promise,然后调用then
方法将继续作为回调传递。 当到达return
,它不会向调用方发出“迭代结束”的信号,而是使用返回值来解析最初返回的promise。
承诺和收益并不是最容易掌握的,尤其是当您不知道它们如何在幕后工作时。 因此,让我们从基础开始。 首先要了解的是Javascript是单线程的,这意味着它只能同时执行一项操作。 您仍然可以“一次”执行多个操作的方式是因为javascript有一个称为事件循环的事件。
事件循环基本上看起来像这样:
while(queue.waitForTasks()) {
queue.performNextTask();
}
事件循环的作用是检查是否有新的“任务”要运行Javascript。 如果有任务。 然后它会执行,直到没有其他任务可以执行为止。 并且它将等待其新任务。 这些任务存储在称为队列的内容中。
承诺,异步/等待
现在我们了解了Javascript如何处理不同的任务。 它如何与promise和async / await一起使用? promise
无非是一项任务,或者在使用Javascript的情况下,某项保留了一项任务,它将被添加到队列中,并在所有任务执行完毕后立即执行。 .then()
是一种为您的promise提供回调的方法,一旦您的resolve回调被调用,该promise将被执行。
await [something]
关键字告诉Javascript,嘿,把下一个[something]
放在队列的末尾,一旦[something]
有结果要给我,请立即联系我。
具有async
关键字的函数基本上是在告诉Javascript:“此函数是一个承诺,但要立即执行”。
异步函数的流程最容易通过两个不同的异步函数A和B来掌握/演示,如下所示:
const A = async () => {
console.log(A: Start);
for (var i = 0; i < 3; i++) {
await (async () => console.log('A: ' + i));
}
console.log('A: Done');
}
const B = async () {
console.log(B: Start);
for (var i = 0; i < 3; i++) {
await (async () => console.log('B: ' + i));
await (async () => {/* A task without output */});
}
console.log('B: Done');
}
像这样在等待时调用函数:
console.log('Executing A');
await A();
console.log('Executing B');
await B();
它会导致:
Executing A
A: Start
A: 0
A: 1
A: 2
A: Done
Executing B
B: Start
B: 0
B: 1
B: 2
B: Done
并运行:
console.log('Executing A');
A();
console.log('Executing B');
B();
会导致:
Executing A
A: Start Note: still gets ran before Executing B
Executing B
B: Start
A: 0
B: 0
A: 1
A: 2 Note: A: 2 first because another task in B was put in the queue
A: Done
B: 1
B: 2
B: Done
了解这一点可能有助于更好地了解您的应用程序流程。
让
yield
关键字类似于await
,在某种意义上,“外部力”控制何时继续执行功能。 在这种情况下,不是完成promise任务,而是generator.next()
函数
尽管我真的很想知道,但我不知道正确的心理模型的答案。
但我发现这很有趣
凯尔·辛普森(Kyle Simpson)的《您不知道JS》一书深入探讨了如何在r / Javascript reddit上进行等待- 来源 :
“这是完全不正确的。生成器不会运行到完成状态,大多数异步await的引擎实现实际上将它们像生成器一样对待。遇到产量时,生成器会在本地暂停……字面上。Await使用相同的方法“。
“不,这都是不正确的废话。大多数引擎将async-await视为生成器,它肯定会以屈服的方式进行局部停顿。 将promise.then()包裹在后续代码中将是实现await的最幼稚且效率最低的方法之一即使发动机做到了(大多数没有),这并不意味着这是正确的心理。模型。就像产量地方暂停是正确的心智模型“。
但是,当我亲自查看ECMA脚本规范并使用vscode nodejs调试器浏览代码时,await似乎更类似于.then()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.