解开 ECMAScript 2020 异步函数背后的奥秘

阅读时长 12 分钟读完

前言

在 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 的实现示例代码:

在这个 fetchData 函数中,我们使用了两个 await 关键字,并且使用 fetch() 方法来获取 JSON 数据。fetch() 返回一个 Promise 对象,而在 await 关键字的控制下,fetch() 将以异步方式运行,直到它返回 JSON 数据。返回的 JSON 数据可以赋值给变量 const data 中,该数据表示异步数据的结果,最后返回一个 data。

现在,我们来看一下 async 函数背后的实现原理。异步函数可能在几个阶段中使用:

1.定义:将 Async 声明为函数,使该函数返回一个 Promise 对象。

2.调用:在函数调用时,返回一个已解决的 Promise 存储在一个变量中,该结果调用了函数体内的相关异步操作。

下面我们来看下面这个 async 的例子:

当我们调用getUser()函数时,返回一个Promise对象:

在调用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

纠错
反馈

纠错反馈