首页 > 解决方案 > 为什么 Async 总是返回一个 Promise?

问题描述

这个问题是理论上的——我没有具体的问题要解决。

话虽如此,为什么async关键字将异步函数的返回值包装在 Promise 中?重点是什么?仅仅是因为await表达式需要一个承诺吗?或者这个决定背后有什么意义/用途?

标签: javascriptasync-await

解决方案


我想我会回答这个问题,主要是因为 Javascript 中的 async 曾经让我感到很困惑,突然间它就崩溃了,所以我希望这个类比可以帮助你实现这一点。

你有一个异步事件。这可能是任何事情,从服务器获取某些东西,在浏览器中做一些需要时间的事情,训练机器学习模型(!),执行使用 setTimeout 的函数或方法等。

Javascript 的美妙之处以及它在浏览器中运行良好的一个关键原因是它以一种非常聪明的方式使用它运行的处理器线程,从而阻止线程被需要时间的进程阻塞(如上面提到的那些)

许多其他语言,例如 Ruby,在多个线程上运行。可以使用服务工作者在 javascript 中的多个线程上运行进程,但这超出了此答案的范围!

JS 事件循环的异步特性允许线程在等待进程完成时“关闭”并执行其他操作。

从编程的角度来看,这样做的问题是,如果代码中依赖于阻塞事件结果的某些内容不等待事件发生,则它可能会因为事件而获得“未定义”。在它尝试使用它的结果之前完成。拿下面这段代码

let scopedVariable
console.log('the code has started')
setTimeout(() => {
  scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)

当代码到达控制台日志时,setTimeout 尚未完成。由于 setTimeout 仅在完成时设置 scopedVariable,所以当我们记录它时该变量是未定义的

但是,如果

我们将超时包装在一个 Promise 中,我们可以等待它的解析回调(Promise 的第一个参数),代码将“暂停”,直到 Promise 在继续之前到达解析回调。

当我们等待 promise 并且 setTimeout 完成时,resolve 函数设置变量,因此当我们控制台记录它时,它会保存来自 promise 的值

let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
  setTimeout(() => {
    resolve(scopedVariable="I have resolved")
  }, 5000);
})
const container = async () => {
  const result = await asyncEvent
  console.log(scopedVariable)
}

container()

您可以交替使用 await 和 .then

例如我们可以去:

let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
  setTimeout(() => {
    resolve(scopedVariable="I have resolved")
  }, 5000);
})
const container = async () => {
  asyncEvent.then(() => console.log(scopedVariable))
}

container()

代码将再次在 .then 处暂停,然后在 asyncEvent 承诺解决后继续。

事实上,如果我们使用 .then 我们不需要将它包含在异步函数中,因此我们可以像这样重写它

let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
  setTimeout(() => {
    resolve(scopedVariable="I have resolved")
  }, 5000);
})

asyncEvent.then(() => console.log(scopedVariable))

.then 的优点在于随附的 .catch 允许您捕获异步事件引发的任何错误(例如,如果在出现错误时从服务器检索某些内容)。对于异步等待,您需要将潜在危险的函数包装在 try catch 中。

为了使用 await 你需要在一个 async 函数中(因此上面的 async 容器函数)。这对于 .then 不是必需的,但是 .then 和 .catch 链会使您的代码变得混乱。

我希望这有帮助!


推荐阅读