我对使用
await
关键字很陌生,我已经习惯了旧的“Promise”,比如 Cypress 中使用的命令。
但是我有一个困惑是当一个函数返回一个承诺时,但是在函数中和使用函数时都有一个
await
关键字。例如:
async goto() {
await this.page.goto('https://playwright.dev');
}
以上是页面对象模型模式中使用的一个简单方法,
page.goto
返回一个承诺,所以我们await
它.....这是有道理的,但要使用它你还必须await
通过:
await playwrightDev.goto();
所以我理解语法,因为
goto
函数是async
。但是我想我并不真正理解“为什么”我们必须这样做。更具体地说,为什么函数必须是async
。因为函数的内部命令已经在等待一个promise,为什么函数本身需要异步。因为无论如何它都不会返回,直到最里面的命令完成?
希望我问的是有道理的。我理解语法,但不知道为什么会这样。
为什么函数本身需要是
。因为,它不会返回,直到最里面的命令完成为止?async
没有。
async
函数确实立即返回:它返回对主体最终返回值的承诺。它执行 async
hronously,暂停并稍后恢复其 own 执行只要有 await
,但这不会影响调用者。
调用者立即得到一个承诺。调用者可以选择继续自己的执行,例如调用其他异步函数并将所有结果承诺传递给
Promise.all
,或者它可以在承诺上调用.then()
,或者它可以即发即弃承诺。或者它可能 await
它暂停自己的执行,直到被调用的函数完成。
一个常见的初学者误解是,一旦您
await
一个承诺,您就可以以某种方式返回到您的主线同步代码。相反,一旦你有了一个承诺,你就会 陷入异步模式 对于依赖于该结果的任何其他代码,无论依赖代码是否是异步的。
这并不是说您不能在此过程中运行同步代码,只是说任何使用 promise 本身的函数基本上都可以传递地成为 promise。
根据经验,每个承诺有 1 个
await
,但需要注意的是,任何使用 await
的异步代码调用者现在都会自己返回承诺。
您不必在方法内部使用
async
/await
,因为手头只有一个承诺。您可以返回 page.goto
返回的承诺,将其传递给调用者并从本质上剥离多余的承诺包装器:
goto() {
return this.page.goto('https://playwright.dev');
}
无论哪种方式,调用者都需要
await
返回的承诺,除非他们不关心导航何时发生并且不需要承诺中的已解决值,通常情况并非如此。
要跟踪您的承诺,请将您的承诺决议放在同一个链中,按顺序发生。链中的每一个 promise 都需要用
await
或用 then
链接。一旦单个 promise 未被 await
ed,链条就会中断,链条的第二部分无法从第一部分获取价值或等待第一部分的完成。
调用代码被 promises“污染”的原因与这样一个事实有关,即 promise 只是异步回调的语法糖(想想
setTimeout
),而回调不会在主线代码中运行。所有同步执行在任何承诺运行之前结束。然后,当承诺稍后解决时,执行恢复,运行“回调”(或在 await
之后或下一个 .then
处理程序中的代码)。
async
/await
只是让它看起来像是你在一个函数中同步完成所有这些,但这只是一个语法技巧,使回调变平。
如果您可以使用
await
,然后恢复主线同步代码,这将是一个非异步的阻塞调用,它会占用单个节点线程(想象一下节点版本的“此页面未响应”,您可能当发生太多受 CPU 限制的阻塞处理并且事件循环没有机会运行和重新绘制屏幕或处理交互时,请在浏览器中查看。