深入探讨了 JavaScript 中 Promise 的内部机制,解释了它们如何使异步任务以非阻塞方式执行,并展示了 Promise 的创建、状态变化以及与事件循环的关系。
正文从这开始~~
JavaScript 中的 Promise 一开始可能会让人感到有些难以理解,但是如果我们能够理解其内部的工作原理,就会发现它们其实是非常易于掌握的。
在这篇博客文章中,我们将深入探讨 Promise 的一些内部机制,并探索它们是如何使得 JavaScript 能够执行非阻塞的异步任务。
一种创建 Promise 的方式是使用 new Promise 构造函数,它接收一个执行函数,该函数带有 resolve 和 reject 参数。
new Promise((resolve, reject) => {
// TODO(Lydia): Some async stuff here
});
当 Promise 构造函数被调用时,会发生以下几件事情:
-
创建一个 Promise 对象。这个 Promise 对象包含几个内部槽,包括
[[PromiseState]]
、
[[PromiseResult]]
、
[[PromiseIsHandled]]
、
[[PromiseFulfillReactions]]
和
[[PromiseRejectReactions]]
。
-
创建一个 Promise 能力记录。这个记录 “封装” 了 Promise,并增加了额外的功能来 resolve 或 reject promise。这些功能可控制 promise 的最终
[[PromiseState]]
和
[[PromiseResult]]
,并启动异步任务。
我们可以通过调用 resolve 来解决这个 Promise,这是通过执行函数可以实现的。当我们调用 resolve 时:
调用 reject 时的过程类似,现在
[[PromiseState]]
被设置为 “已拒绝”(rejected),并且
[[PromiseResult]]
被设置为我们传递给 reject 的值,这是 “失败!”(Fail!)。
当然很好。但是,使用函数来改变对象内部属性有什么特别的呢?
答案就在与我们目前跳过的两个内部槽相关的行为中:
[[PromiseFulfillReactions]]
和
[[PromiseRejectReactions]]
。
[[PromiseFulfillReactions]]
字段包含 Promise Reactions。这是一个通过将 then 处理程序链接到 Promise 而创建的对象。
此 Promise Reaction 包含一个
[[Handler]]
属性,其中包含我们传递给它的回调。当 promise resolve 时,该处理程序会被添加到微任务队列中,并可访问 promise 解析时的值。
当 promise 解析时,这个处理程序接收到
[[PromiseResult]]
的值作为其参数,然后将其推送到 Microtask Queue 微任务队列。
这就是 promise 的异步部分发挥作用的地方!
微任务队列是事件循环(event loop)中的一个专门队列。当调用栈(Call Stack)为空时,事件循环首先处理微任务队列中等待的任务,然后再处理来自常规任务队列(也称为 “回调队列” 或 “宏任务队列”)的任务。
如果你想了解更多,可以查看我的事件循环视频!
类似地,我们可以通过链式 catch 来创建一个 Promise Reaction 记录来处理 Promise Reject。当 Promise 被拒绝时,这个回调会被添加到微任务队列。
到目前为止,我们只是在执行函数内直接调用 resolve 或 reject。虽然这是可能的,但它并没有充分利用 Promise 的全部功能(和主要目的)!
在大多数情况下,我们希望在稍后的某个时间点(通常是异步任务完成时)进行 resolve 或 reject。
异步任务在主线程之外执行,例如读取文件(如 fs.readFile)、提出网络请求(如 https.get 或 XMLHttpRequest),或者像定时器(setTimeout)这样简单的任务。
当这些任务在未来某个未知的时间点完成时,我们可以使用此类异步操作通常提供的回调功能,要么使用异步任务返回的数据进行 resolve,要么在发生错误时进行 reject。
为了直观地说明这一点,让我们一步步来执行。为了让这个演示简单但仍然真实,我们将使用 setTimeout 来添加一些异步行为。
new Promise((resolve) => {
setTimeout(() => resolve("Done!"), 100);
}).then(result => console.log(result))
首先,将 new Promise 构造函数添加到调用栈,并创建 Promise 对象。