等待同步函数是否同步返回值?

18

我从一个我不熟悉的内部工具库导入了一个函数。这个库没有文档,我只是因为它的名称getUserDetails而假设它是异步的。我认为它正在执行一个 http 请求。

我像这样在异步函数中使用它

async function getAllUserInfo(event) {
    const details = await getUserDetails(event);
    // other stuff
}

我在我的假设上错了。一位同事指出它不是异步的。最终我做出了更改,但当我错误地使用它时,它仍然有效。我能够等待同步函数并返回正确的数据。

我的问题是关于它如何工作的。在同步函数前加上await会让它在下一个tick中解决,还是会像同步函数一样立即返回?

2个回答

49
它能够工作是因为 await 不要求它的操作数是一个 promise!如果等待的表达式不是 promise,它将返回该表达式的值。

请参阅 等待运算符的文档

最重要的部分是:

[rv] = await expression;
  • expression: 一个Promise或任何要等待的值。
  • rv: 返回Promise的完成值,或者如果它不是Promise,则返回该值本身。

在您的情况下,getUserDetails没有返回一个Promise,而是一些常规的用户详细信息,因此await表达式只返回这些详细信息,就好像运算符根本不存在一样。

但是,尽管getUserDetails是同步的,在您的异步函数之前加上await仍会将控制权交给其调用者,并且await后面的“回调部分”稍后会被执行。以下是一个示例脚本:

function f() {
  console.log('In f');
}

async function g() {
  console.log('Starting g');
  await f();
  console.log('Finishing g');
}

console.log('Starting the script')
g();
console.log('Finishing the script')

注意脚本的输出:

$ node asynctest.js 
Starting the script
Starting g
In f
Finishing the script
Finishing g

注意观察 await 调用是如何“暂停”了不能在主块完成之前恢复的 g 函数的!因此,await 的确产生了效果。如果您没有放置 await,则会在“Finishing the script”之前看到“Finishing g”。尝试一下吧!

顺便说一下,产生这种效果的原因是,即使 await 可以接受一个不产生 Promise 的表达式,JavaScript 也会将非 Promise 操作数立即转换为已解析出该值的 Promise。因此,仍然会创建一个 Promise,并将 await 后面的部分视为回调函数,直到当前执行流程完成后才能运行。


4
有趣。感谢您指引我查看文档。我起初忽略了这行文字:如果 await 操作符后面的表达式不是 Promise,它会被转换为一个已解决的 Promise。 因此,如果我理解正确的话,如果使用 await 在同步函数前面,它会异步返回该值。 - skellertor
3
同意,非常好。我已经详细说明了我的答案,并包含了一个你可以运行的示例。希望它能澄清事情。如果不能,请告诉我,我会再试一次。 - Ray Toal
完美。谢谢! - skellertor
@RayToal,那么等待预期为函数的任何用户输入是一种不好的做法吗? - Normal
抱歉,您所说的“预期为函数的用户输入”是什么意思? - Ray Toal

8

如果你等待的值不是一个Promise,它将通过使用Promise.resolve转换为已解决的Promise。

function sync(){
 return 1
}

(async ()=>{
  const v = await sync(); console.log(v)
})();

(async ()=>{
  const v = await Promise.resolve(sync()); console.log(v)
})()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接