前言
在 Web 前端开发中,异步函数是一个非常重要的概念。它可以帮助我们处理异步操作,比如网络请求、文件读取等,以保证 JavaScript 前端程序的流畅性和效率性。同时,ES6 也为我们带来了异步函数这个新特性,用于简化异步操作的实现,其中包括异步等待 await 和异步生成器 async/await。那么,今天我们来深入探究 ECMAScript2020中异步函数背后的实现原理。
什么是异步函数
异步函数是一个很常用的概念,指的是实现异步编程的一种方式。通常来说,编写异步代码,我们需要使用回调函数或者Promise对象,从而解决异步操作和同步操作之间的进程问题。
举个例子,假设我们在前端页面上发送一个 AJAX 请求:
-- -------------------- ---- ------- -------- ---- -------------- ------- ------ -------- ---------------- - -------------------- -- ------ ------------- ------- ------ - ------------------- - ---展开代码
在这个例子中,我们通过回调函数来实现异步操作,当服务器响应完成后,调用了success回调函数,并将响应结果传入到它的参数中。这个方式存在一些致命的缺陷:回调地狱、代码难以读懂等等,但是ES6给我们带来了异步函数,用它可以大幅度地改善这些问题。我们来看下面这个例子:
-- -------------------- ---- ------- ----- -------- ----------- - --- - ----- -------- - ----- --------------------- ----- ---- - ----- ---------------- ------------------ - ----- ------- - ------------------- - -展开代码
在这段代码中,我们使用了一个异步函数getUser,并且在其中使用await来等待异步调用结束。这使得代码更加简洁,可读性更好,可以避免回调地狱和异步编程问题。当函数执行完毕后,会返回一个promise对象,其中包含对应异步操作的结果,如果操作出现问题,会抛出异常并被try-catch捕获。现在,我们可以上手来探究异步函数背后的奥秘啦!
Promise 的实现原理
要想理解异步函数,我们首先要了解 Promise 的实现原理,因为 Promise 是异步函数背后的基础。
Promise 顾名思义,它是一种解决JavaScript异步编程的方式,Promise 对象两种状态:完成或失败。或者,更准确地说,Promise 有三种状态,因为执行 Promise 的代码可以分为以下几种状态之一:
- pending(等待态): 初始状态,既不是成功,也不是失败状态。换言之,就是Promise实例创建时所处的状态;
- fulfilled(成功态): 意味着操作成功完成,Promise将异步操作的结果作为参数传递给它的回调函数 then() ;
- rejected(失败态):意味着操作失败,Promise将异步操作抛出的错误作为参数传递给它的回调函数 catch() 。
现在,我们来看下Promise的实现原理:
-- -------------------- ---- ------- ----- ------- - -------------------- - ----------- - --------- -- ----- ----------------- - --------- -- ----------- --------------- - --------- -- ----------- ----- ------- - ------- -- - -- ------ -- ------------ --- ---------- ------ ----------- - ----------- ----------------- - ----- - ----- ------ - ------- -- - -- ------ -- ------------ --- ---------- ------ ----------- - ---------- --------------- - ----- - --- - -- ---- ----------------- ------- - ----- --- - --------- - - ----------------- ----------- - -- ------------ --- ------------ - -- ------------------------------------ ------------------------------ - -- ------------ --- ----------- - -- ----------------------------------- --------------------------- - - ----------------- - -- ------------- -- ------------ --- ----------- - --------------------------- - - -展开代码
在这个代码片段中,我们通过构造函数创建了一个 Promise 对象。在 Promise 对象创建时,它的状态设置为 pending,并且初始化 successValue 和 errorValue 值为空。Promise 构造函数需要接收一个executor函数,该函数接受两个参数:resolve 和 reject。这两个回调函数用于在异步操作完成时处理 Promise 的状态值。
当异步操作完成,Promise 处于 fulfilled 状态,把异步操作的结果传递给成功回调函数,在异步操作失败时,将异步操作返回的错误作为参数传递给失败回调函数,并将 Promise 对象状态设置为 rejected。
Promise 的 then 方法接收两个函数,即 onFulfilled 和 onRejected。这样,一旦 Promise 成功解决,就会调用 onFulfilled 函数,如果 Promise 失败,就会调用 onRejected 函数。catch 方法用于在 Promise 抛出错误时捕获异常,它会接收一个函数参数,该函数将错误捕获作为参数。
Async/await的实现原理
现在我们回到异步函数方面,异步函数背后实现的最重要的部分是 Promise,通过 Promise 异步函数返回一个 Promise 对象,可以帮助我们以 Promise 的方式处理异步操作。下面我们来深入探究一下 Async/await 的实现原理。
在 JavaScript 中,async 是一个关键字,它用于修饰函数,使函数返回一个 Promise 对象,这个 Promise 对象在函数执行完毕后会自动 resolve。
在函数中使用 await 关键字,可以让 JavaScript 引擎暂停函数的执行,等待 Promises 完成,并返回结果。在这个过程中,JavaScript 执行其他任务,直到 Promise 执行完毕,之后继续执行这个函数中等待的下一条语句。
下面,我们来看一个简单的 Async/await 的实现示例代码:
async function fetchData(url) { const response = await fetch(url); const data = await response.json(); return data; }
在这个 fetchData 函数中,我们使用了两个 await 关键字,并且使用 fetch() 方法来获取 JSON 数据。fetch() 返回一个 Promise 对象,而在 await 关键字的控制下,fetch() 将以异步方式运行,直到它返回 JSON 数据。返回的 JSON 数据可以赋值给变量 const data 中,该数据表示异步数据的结果,最后返回一个 data。
现在,我们来看一下 async 函数背后的实现原理。异步函数可能在几个阶段中使用:
1.定义:将 Async 声明为函数,使该函数返回一个 Promise 对象。
2.调用:在函数调用时,返回一个已解决的 Promise 存储在一个变量中,该结果调用了函数体内的相关异步操作。
下面我们来看下面这个 async 的例子:
async function getUser(id) { const response = await fetch(`/user/${id}`); const data = await response.json(); return data; }
当我们调用getUser()函数时,返回一个Promise对象:
const userPromise = getUser(12345);
在调用getUser()函数时,函数首先声明为 async 函数,返回一个Promise对象,异步地执行异步操作。JavaScript引擎需要调用异步操作来捕获异步行为并返回结果,因此在getUser()函数中,可以使用await等待操作完成,并将结果返回给调用方。
async函数背后的实现方式:async函数会在定义子执行时被转换为普通函数。异步函数体内的await语句会被转换为Promise.then 或Promise.catch 的调用,从而实现异步操作的等待。同时,async 返回的值将作为 promise 对象的解决值。
下面是一个Async/await的实现:
-- -------------------- ---- ------- -------- ------------------------------- - -- -------- ------ ---------- - -- ---------------- ----- --- - ------------------------- ---------- -- ------ - ------- -- ----- ---- ------ --- ----------------- ------- -- - -- ------ --- ---- -------- -------- --------- ---- - --- --------------- --- - -- ------- --- --------- --- --- --- ---- ----- --------------- - ------------- - ----- ------- - -- -- -- ----- -- ------ ------- -- -- -- - ------ ------ ------------- - -- --- --- ----- --- -- --- --------- ------ ----- - ------ ---- - - --------------- -- ------ --- --------- ------ -------- -- ------ - ------ -------------- - ---- - -- ----- --- ---- ------- --- ------ ------- --- ------- ------ ---------------------------- -------- -------------- - ------------ ---- -- -------- ------------- - ------------- ---- - - - - -- ---- ------ --- --------- ----- ------------ -- - -展开代码
在这个代码片段中,我们定义了一个 asyncToGenerator 函数,该函数接收一个生成器函数作为参数,并返回另一个函数。所有存储在 async 函数中的 await 语句会被翻译成 Promise.resolve 调用,然后在 then 或 catch 中处理结果,以实现异步操作的暂停和恢复。
结论
异步函数是 JavaScript 编程语言中一种实现异步行为的重要机制,异步函数的后面是基于 Promise 实现的。async 和 await 提供了使用异步的方式,使得异步编程的设计更加紧凑和容易阅读。现在,我们已经介绍异步函数以及异步函数背后的实现方式,并且可以在日常工作和学习中使用它们了。
完整代码(对应前面的asyncToGenerator函数的代码):
-- -------------------- ---- ------- -------- ------------------------------- - -- -------- ------ -------- -- - -- ---------------- ----- --- - ------------------------- ---------- -- ------ - ------- -- ----- ---- ------ --- ----------------- ------- -- - -- ------ --- ---- -------- -------- --------- ---- - --- --------------- --- - -- ------- --- --------- --- --- --- ---- ----- --------------- - ------------- - ----- ------- - -- -- -- ----- -- ------ ------- -- -- -- - ------ ------ ------------- - -- --- --- ----- --- -- --- --------- ------ ----- - ------ ---- - - --------------- -- ------ --- --------- ------ -------- -- ------ - ------ -------------- - ---- - -- ----- --- ---- ------- --- ------ ------- --- ------- ------ ---------------------------- -------- -------------- - ------------ ---- -- -------- ------------- - ------------- ---- - - - - -- ---- ------ --- --------- ----- ------------ -- - -展开代码
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/66fb7c4a44713626015d8e1c