返回

异步函数,async,await那些你忽略的进程

前端

前言

在现代 JavaScript 中,异步编程是一个非常重要的概念。它允许我们编写出更加响应式和高效的代码。异步函数、async 和 await 是 JavaScript 中用于编写异步代码的强大工具。它们使我们能够以一种更加清晰和简洁的方式来处理异步任务,而无需使用回调函数或 Promise。

事件循环

为了理解异步函数是如何工作的,我们首先需要了解 JavaScript 的事件循环。事件循环是一个不断运行的循环,它负责处理 JavaScript 代码的执行。它由以下几个部分组成:

  • 主线程:这是 JavaScript 代码执行的主线程。它负责执行同步任务,即那些不需要等待其他任务完成的任务。
  • 任务队列:这是存储等待执行的异步任务的队列。当异步任务完成时,它会被添加到任务队列中。
  • 微任务队列:这是存储等待执行的微任务的队列。微任务是比异步任务优先级更高的任务,它们会在主线程执行完所有同步任务和异步任务之后执行。

异步函数

异步函数是一种特殊的函数,它允许我们编写异步代码,而无需使用回调函数或 Promise。异步函数使用 async 关键字来声明,它返回一个 Promise 对象。

async function myAsyncFunction() {
  // 异步代码
}

当调用异步函数时,它会立即返回一个 Promise 对象。这个 Promise 对象表示异步操作的结果。当异步操作完成时,Promise 对象就会被解析,并且异步函数会继续执行。

await 关键字

await 关键字用于等待异步操作完成。它只能在异步函数中使用。当 await 关键字后面跟着一个 Promise 对象时,它会暂停异步函数的执行,直到 Promise 对象被解析。

async function myAsyncFunction() {
  const result = await myPromise;

  // 使用 result
}

在上面的示例中,myAsyncFunction 函数等待 myPromise Promise 对象被解析。一旦 myPromise 被解析,myAsyncFunction 函数就会继续执行,并且 result 变量将包含 myPromise 的结果。

事件循环与异步函数

异步函数和事件循环是紧密相关的。当调用异步函数时,它会立即返回一个 Promise 对象。这个 Promise 对象会被添加到任务队列中。当主线程执行完所有同步任务和异步任务之后,它会从任务队列中取出 Promise 对象并执行它。当 Promise 对象被解析时,异步函数会继续执行。

微任务队列与异步函数

微任务队列与异步函数也是紧密相关的。当异步函数中的 await 关键字后面跟着一个 Promise 对象时,它会暂停异步函数的执行,直到 Promise 对象被解析。一旦 Promise 对象被解析,它会被添加到微任务队列中。当主线程执行完所有同步任务、异步任务和微任务之后,它会从微任务队列中取出 Promise 对象并执行它。当 Promise 对象被解析时,异步函数会继续执行。

Promise

Promise 是 JavaScript 中用于处理异步操作的内置对象。它表示一个异步操作的结果。当异步操作完成时,Promise 对象就会被解析,并且包含异步操作结果。

const myPromise = new Promise((resolve, reject) => {
  // 异步代码

  if (success) {
    resolve(result);
  } else {
    reject(error);
  }
});

在上面的示例中,myPromise 是一个 Promise 对象。当异步代码执行完后,它会调用 resolve() 函数或 reject() 函数来解析 Promise 对象。如果异步操作成功,则调用 resolve() 函数,并将结果作为参数传递给它。如果异步操作失败,则调用 reject() 函数,并将错误作为参数传递给它。

回调函数

回调函数是一种在异步操作完成时被调用的函数。它通常作为参数传递给异步函数。当异步操作完成时,异步函数会调用回调函数,并将结果作为参数传递给它。

function myAsyncFunction(callback) {
  // 异步代码

  callback(result);
}

myAsyncFunction(function(result) {
  // 使用 result
});

在上面的示例中,myAsyncFunction 函数接受一个回调函数作为参数。当异步操作完成时,myAsyncFunction 函数会调用回调函数,并将结果作为参数传递给它。

生成器函数

生成器函数是一种特殊的函数,它可以暂停和恢复执行。它使用 yield 关键字来暂停执行。当生成器函数被调用时,它会返回一个生成器对象。生成器对象可以被多次调用来恢复执行。

function* myGeneratorFunction() {
  // 代码
  yield;
  // 代码
}

const generator = myGeneratorFunction();

generator.next(); // 暂停执行
generator.next(); // 恢复执行

在上面的示例中,myGeneratorFunction 函数是一个生成器函数。当它被调用时,它返回一个生成器对象。当生成器对象被调用时,它会暂停执行。当再次调用生成器对象时,它会恢复执行。

协程

协程是一种轻量级的线程。它与线程不同,协程不需要单独的栈空间。协程可以在同一个线程中运行,并且可以暂停和恢复执行。

function myCoroutine() {
  // 代码
  yield;
  // 代码
}

const coroutine = myCoroutine();

coroutine.next(); // 暂停执行
coroutine.next(); // 恢复执行

在上面的示例中,myCoroutine 函数是一个协程。当它被调用时,它返回一个协程对象。当协程对象被调用时,它会暂停执行。当再次调用协程对象时,它会恢复执行。

使用异步函数的最佳实践

以下是使用异步函数的一些最佳实践:

  • 避免在异步函数中使用同步代码。同步代码会阻塞事件循环,导致其他任务无法执行。
  • 尽量使用 await 关键字来等待异步操作完成。使用 await 关键字可以使代码更加清晰和简洁。
  • 避免在异步函数中使用回调函数。回调函数会使代码更加难以理解和维护。
  • 尽量使用 Promise 对象来处理异步操作。Promise 对象可以使代码更加清晰和简洁。
  • 避免在异步函数中使用生成器函数或协程。生成器函数和协程会使代码更加难以理解和维护。

结论

异步函数、async 和 await 关键字是 JavaScript 中用于编写异步代码的强大工具。它们使我们能够以一种更加清晰和简洁的方式来处理异步任务,而无需使用回调函数或 Promise。通过理解事件循环、任务队列和微任务队列是如何协同工作以执行异步任务的,我们可以编写出更加健壮、更可维护的异步代码。