await 关键字的详细介绍

await 关键字是异步编程中用于等待 Promise 对象解决的一种机制。其作用是暂停异步函数的执行,直到 Promise 对象变为已完成状态(即成功解决或失败拒绝),才会继续往下执行。这样可以避免异步代码执行时发生出错或异常情况,让程序具有更好的稳定性。

具体来说,当遇到 await 关键字时,JavaScript 引擎会将后面的操作封装为一个 Promise 对象,并将整个异步函数的执行权交还给宿主环境。然后引擎会去执行其他的 JavaScript 代码,直到 Promise 对象被解决。如果 Promise 对象被解决了,那么 JavaScript 引擎会恢复异步函数的执行,将 Promise 对象的 resolve() 或 reject() 的返回值作为 await 表达式的结果,并继续往下执行异步函数后面的代码。

由于 await 关键字在 Promise 对象解决前会一直等待,因此可以保证异步函数后面的代码在 Promise 对象解决后才被执行。这样就可以保证代码的顺序性,避免异步操作的返回值未知而导致程序出现错误行为。

从事件循环的角度上分析,await 关键字会将其后面的 Promise 对象加入到当前微任务队列中,在当前宏任务执行完毕后,JavaScript 引擎会优先执行微任务队列中的所有任务,然后才会去执行下一个宏任务。因此,当遇到 await 关键字时,JavaScript 引擎会暂停当前宏任务的执行,等到 Promise 对象被成功解决为止,然后再去执行余下的代码。

具体来说,当暂停异步函数的执行时,JavaScript 引擎会将该函数添加到正在执行的宏任务队列中,并将异步函数内部的微任务队列清空。接下来,如果异步函数中遇到了 await 关键字,JavaScript 引擎会先将 await 关键字后面的 Promise 对象加入到微任务队列中,然后交还执行权。

执行权交还给宿主环境,宿主环境会在后面的一段时间内处理宏任务队列中的任务。如果异步函数中所有同步任务都已执行完毕,而 Promise 对象还没有被解决,那么 JavaScript 引擎就会等待 Promise 对象解决,并在 Promise 对象被成功解决后将该 Promise 对象加入到微任务队列中。执行到微任务队列时,该任务才会被执行,即调用 resolve() 或 reject() 方法后将异步函数后面的代码加入到微任务队列中。

总之,await 关键字使得 JavaScript 引擎可以将宏任务任务拆分成微任务来执行,避免了一些执行上下文的切换,提高了执行效率,同时还可以保证异步函数中后面的代码在前面的 Promise 对象解决后才被执行。

假设我们需要模拟一个异步操作,它会在一段时间后返回一个随机数,并输出一条日志消息:

async function simulateAsync() {
  console.log('Start simulateAsync function');
  const randomDelay = Math.random() * 5000; // 随机生成 0~5000ms 的延迟时间
  await new Promise(resolve => setTimeout(resolve, randomDelay));
  const randomNumber = Math.floor(Math.random() * 100);
  console.log(`Random number generated: ${randomNumber}`);
  console.log('End simulateAsyn function');
}

console.log('Before calling simulateAsync function');
simulateAsync();
console.log('After calling simulateAsync function');

我们定义了一个名为 simulateAsync 的异步函数,该函数会随机生成一个延迟时间(以毫秒为单位),在这个延迟时间过后输出一个随机数和日志消息。然后我们在外部代码中,通过 console.log 打印出一系列日志消息,然后调用 simulateAsync 函数。我们来分析一下这段代码的执行顺序:

  1. 当外部代码执行时,首先会打印一条日志消息 Before calling simulateAsync function,然后调用 simulateAsync 函数。
  2. 在函数内部,首先会输出一条日志消息 Start simulateAsync function,表明函数已经开始执行。
  3. 接着,我们使用了 await 关键字等待一个 setTimeout 方法返回的 Promise 对象。该方法会在随机生成的时间后,将 Promise 对象的状态设置为已解决(resolved)。
  4. 在等待期间,由于 simulateAsync 函数已经暂停执行,JavaScript 引擎会将控制权交还宿主环境,以便宿主环境能执行其他任务。
  5. 如果在等待期间,宿主环境需要执行其他的异步任务,则引擎会将它们添加到微任务队列或宏任务队列中,等待异步任务完成后,再将其处理掉。但在这个例子中,我们没有添加其他的异步任务。
  6. 当 Promise 对象被解决时,JavaScript 引擎会将 console.log 的两条日志消息添加到微任务队列中,并在下一个宏任务执行前,按顺序将其执行。
  7. 在打印消息 Random number generated 后,我们又输出了一条日志消息 End simulateAsync function,表明函数已经执行完毕。
  8. 因此,当所有异步任务都被处理完毕后,JavaScript 引擎会顺序执行微任务,输出消息 Random number generated: xx 和 End simulateAsync function,然后执行下一个宏任务。
  9. 于是,最终会输出以下日志消息:
Before calling simulateAsync function
Start simulateAsync function
After calling simulateAsync function
Random number generated: xx
End simulateAsync function

从上面的例子中,我们可以看到,await 关键字的作用在于将异步操作转换为一个 Promise 对象,然后让 JavaScript 引擎能够在其完成之后,再去执行后面的代码。同时,Promise 的状态改变(即 resolve() 或 reject() 被调用)时,JavaScript 引擎会将相应的处理代码添加到微任务队列中,以便在下一个宏任务执行前按顺序执行。这一过程正是事件循环的核心特性之一。

你可能感兴趣的:(javascript,开发语言,ecmascript)