JavaScript 中的微任务与 ES6 的 Promise

在 JavaScript 中,异步操作是非常常见的,例如网络请求、定时器和事件处理等。在回调函数被广泛应用之后,ES6 推出了 Promise,进一步改善了异步编程的体验。而 Promise 是如何实现异步操作的呢?它与 JavaScript 的微任务有什么关联呢?在本文中,我们将会详细探讨这些问题。

什么是异步操作

在 JavaScript 中,异步操作指的是一些操作不会立即得到结果,而会在后续才会完成,并且该操作会先返回一个 Promise 对象,以便我们在得到结果的时候进行处理。例如:

-------------------------------------
  -------------- -- ----------------
  ---------- -- ------------------
  ------------ -- ----------------------

在上述代码中,我们使用 fetch 函数来获取数据并返回一个 Promise 对象。当数据得到响应后,我们将其转化为 JSON 格式并输出到控制台。如果在获取数据的过程中出现了错误,则会输出相关的错误信息。

Promise 的实现原理

Promise 对象是如何实现异步操作的呢?在执行异步操作时,用于处理异步操作的代码被称为微任务(microtask)。微任务是通过一个叫做 Promise 内部作业队列(job queue)的数据结构来进行存储的。当使用 Promise7 来完成异步操作时,就有可能向作业队列添加一个微任务。

代码片段如下:

----- --------- - --- --------------- -- -----------
----------------- -- -------------------- -------------

-------------------- ---- --------- ----------

对应的控制台输出:

------- ---- --------- -------
------- ---------

在上述代码中,我们首先创建了一个 Promise 对象,并在 .then() 方法中输出了一个字符串。接着,我们在同步执行上下文中输出一段文字。在执行的过程中,推入微任务队列,异步执行回调,所以会先输出同步执行的内容而后输出 Promise 中的结果。

Promise 的状态和生命周期

Promise 的生命周期包括三种状态:

  • pending (待定): 初始状态或者执行中的状态。
  • fulfilled (已完成):表示Promise成功完成执行操作。
  • rejected (已失败):表示Promise未能完成执行操作。

在 Promise 执行完成后,不管是成功的完成还是失败的结束,都将产生一个值。而这个值也被称为 Promise 的结果。如果 Promise 执行成功,将会传递返回值给 then 方法的第一个参数;如果 Promise 执行失败,将会传递错误信息给 catch 方法的回调函数或者是 then 方法的第二个参数。

代码示例如下:

----- --------- - --- ----------------- ------- -- -
    ------------- -- -
        -- -------------- - ---- -
            ---------------- ------------
        - ---- -
            --------------- ------------
        -
    -- ------
---

---------
    --------- -- -----------------
    ---------- -- --------------------

在上述代码中,我们创建了一个 Promise 对象并在 1s 后返回一个随机结果。当结果小于 0.5 的时候,我们将 Promise 转化为 resolved 状态,反之则将其转化为 rejected 状态,并将结果传递给 then 方法或 catch 方法的回调函数。

微任务与宏任务

在前面的文章中,我们简单地介绍了 Promise 的内部作业队列。而这个作业队列是通过 JavaScript 中的两个队列实现的:微任务队列和宏任务队列。

在 JavaScript 中,存在两种类型的任务:宏任务和微任务。当前的回调函数始终位于微任务队列中,而在浏览器内部,微任务队列位于事件循环的过程的“执行被选择的微任务”步骤中。换句话说,只有当前执行的宏任务完成后,传递给当前代码的所在队列的所有微任务才会执行。

下面是一个例子展示了 Promise 与微任务的关系:

--- ----------------- -- -
    -------------------- -----------
    ----------
--
-------- -- -------------------- ------------

-------------------- --------- ----------

在此代码中,我们创建一个 Promise 对象,并在 .then() 方法中输出一个字符串。这个 Promise 对象处于待定状态时,我们打印了一些文本并将其立即解析。接着在同步执行上下文中输出一段文字。在执行回调时,会把这个回调加入到当前上下文的微任务队列中,而在整个 EventLoop 执行完成之前,它都不会执行。因此,输出的先后顺序分别是:

-- -------
------- --------
------- --------- -------
------- --------

Promise 和 async/await 的关系

在 ES8 中引入了 async/await 语法,从而进一步简化了异步编程。在这个模型中,我们使用 async/await 语法来避免回调的嵌套,同时可以直接监视 Promise 的结果。

下面是一个展示 Promise 和 async/await 的关系的例子:

-------- ----------- -
  ------ --- --------------- -- -
    ------------- -- ------------- --------- ------
  --
-

----- -------- ------------- -
  ---------------------

  ----- ------ - ----- ------------
  --------------------

  -------------------
-

--------------

在此代码片段中,我们使用异步 asyncTask 函数获得了一个 Promise 对象。使用 async/await 语法等待异步任务的结果。在函数执行期间,console.log() 输出“Start”和“End”文本,同时在异步任务完成后打印任务结果。

在 async/await 中,await 操作符会将当前代码挂起,直到所等待的 promise 被解析。因为它自身是一个异步操作,所以 await 后面的代码被添加到微任务队列中并在下一个微任务中执行。

结论

在本文中,我们详细探讨了 JavaScript 中的微任务和 ES6 的 Promise,这也是现代 JavaScript 异步编程的核心。我们介绍了如何使用 Promise 以及如何在 async/await 中使用它,同时也深入解释了微任务队列和 EventLoop 的概念,进一步提高了 JavaScript 操作异步操作的水平。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/67329ace0bc820c5823df2d4