【问题标题】:How is async/await working in this code snippet?async/await 如何在此代码段中工作?
【发布时间】:2021-11-29 15:02:30
【问题描述】:

我正在阅读 MDN,了解异步代码如何与 javascript 一起工作。他们提出了这个例子:

async function myFetch() {
  let response = await fetch('coffee.jpg');

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  let myBlob = await response.blob();

  let objectURL = URL.createObjectURL(myBlob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

myFetch()
.catch(e => {
  console.log('There has been a problem with your fetch operation: ' + e.message);
});

函数 myFetch() 被定义,然后以 Promise 方式调用。函数究竟是如何执行的?这是一个异步函数,所以我假设它被赋予某个线程来执行而不干扰运行 Javascript 代码的单个线程? 同样在他们说的文档中等待是停止该行的代码直到完成?所以该函数不能由主应用程序线程执行,因为它不再是异步代码了? 所以这些是我的问题,我想在开始编写实际代码之前确定我理解这个机制。谢谢!

【问题讨论】:

标签: javascript asynchronous async-await


【解决方案1】:

使用 async/await 与使用 Promise 非常相似,async 函数会为您返回一个 Promise,该 Promise 可以具有三种状态之一(Pending、Fulfilled、Rejected)

默认情况下,返回的 Promise 是 Fulfilled (Resolved) 类似于:

Promise.resolve().then(_ => { //... })

使用 await,Promise 将等待您等待的代码

new Promise((resolve, reject) => {
  // ... some tasks here
  resolve(resolvedData);
})
.then(_ => { //... })

当自动或手动抛出错误时,Promise 将被拒绝

new Promise((resolve, reject) => {
  reject(); // Reject manually
})
.then(_ => { })
.catch(e => { //... })

默认情况下,所有 JavaScript 代码都在主线程中执行,这是 JavaScript 唯一拥有的线程

看下面的例子:

async function c() {
    const t = await Array.from(Array(100000000).keys())
}
c().then(t => console.log('I am waiting for the Promise to be fulfilled!'))
console.log('Hey, I don\'t need to wait');

你会得到预期的输出:

"Hey, I don't need to wait"
"I am waiting for the Promise to be fulfilled!"

但是两个日志都在等待填充这个100000000数组,所以现在主线程很忙,阻塞了先完成这个任务,JavaScript本身也无能为力,

另一个例子:

async function c() {
    const t = await fetch('https://jsonplaceholder.typicode.com/photos')
}
c().then(_ => console.log('I am waiting for the Promise to be fulfilled!'))
console.log('Hey, I don\'t need to wait');

在这里你会得到相同的结果,但Hey, I don't need to wait 会立即被记录,只有I am waiting for the Promise to be fulfilled! 会在等待,

为什么?这里我们需要参考一些重要的东西来阅读更多关于(事件循环、调用堆栈、微和宏任务)

简单来说,promise 的行为是一样的,它正在等待某些东西,代码中的第一个控制台日志正在等待 promise 被实现,

不同之处在于,我们现在等待实现 promise 的任务不是线程阻塞的,它不受 JavaScript 本身控制

获取任务由事件循环观察到的外部 API 管理,在这种情况下,调用堆栈不会忙,主 JavaScript 线程可以继续工作,添加将解决承诺的代码到一个队列,当外部API完成抓取任务时,将被事件循环推送到调用堆栈

【讨论】:

  • 所以在执行 I/O 时我应该只使用现代 API 的 await 吗?那么 await fetch 会阻塞该行的代码,然后 main 会在另一个示例中执行更多代码?
  • 我尝试编写一个 Node js 后端,所以所有这些对我来说都很重要,因为我希望我的服务器尽可能地响应并且在我不应该的地方使用 await 会给我一些糟糕的性能我认为
【解决方案2】:

函数 myFetch() 被定义,然后以 Promise 方式调用。

它像任何其他函数一样被调用。所有async 函数都将返回值包装在Promise 中,因此有thencatchfinally 方法可用。

函数究竟是如何执行的?

像使用任何其他函数一样使用()

这是一个异步函数,所以我假设它被分配给某个线程来执行而不干扰运行 Javascript 代码的单个线程?

没有必要让它在其他线程上运行。正如您所说,JavaScript 执行发生在单个线程中。

他们还在文档中说 await 会停止该行的代码直到完成?

是的,在那个异步函数中。

所以该函数不能由主应用程序线程执行,因为它不再是异步代码了?

不必涉及任何线程。只有fetchblob 操作是异步执行的。在等待这些操作时,事件循环和异步函数外部代码的执行将继续。如果您在myFetch 的调用下方有代码,那么它将在等待fetch 时执行,除非您等待对myFetch 本身的调用。

asyncawait 是 Promises 之上的语法糖,因此您的代码实际上正在执行以下操作:

fetch('coffee.jpg').then(response => {
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  response.blob().then(myBlob => {
    let objectURL = URL.createObjectURL(myBlob);
    let image = document.createElement('img');
    image.src = objectURL;
    document.body.appendChild(image);
  });
}).catch(e => {
  console.log('There has been a problem with your fetch operation: ' + e.message);
});

【讨论】:

  • "不必涉及任何线程。它只是异步执行的 fetch 和 blob 操作。在等待这些操作时,事件循环和外部代码的执行异步功能将继续” - 这两者如何共存?你没有线程,所以主线程执行I/O,那它怎么还能进入代码的其他部分并执行更多代码?
  • 还是要看你发给我的链接,谢谢!
  • 例如,浏览器可以对 I/O 进行系统调用,然后在等待操作系统结果的同时继续执行 JavaScript。
  • 好的,所以它仍然涉及一些线程,只是它不受您的代码控制?我的意思是浏览器工作者?我不太了解浏览器方面,我使用这个代码库来构建一个带有节点的后端,我想要好的性能,这就是我问那些阻塞/非阻塞问题的原因
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 2020-10-11
  • 2020-09-06
  • 2020-03-09
  • 2011-06-12
  • 2021-12-18
相关资源
最近更新 更多