Promise源码分析
Promise的底层是浏览器实现的,所以我只是实现了里的功能,并非和源码一摸一样。下面为部分源码,完整源码移驾到github中下载:
https://github.com/young-monk/my-promise.git
我们首先来定义一个MyPromise类,我在这里使用立即执行函数的方式,防止变量全局污染。
const MyPromise = (() => {
//在这里定义内部变量
return class {
//构造器
constructor(executor) {
//...
}
})()
接下来,定义一些内部使用的变量:我这里使用了Symbol,使用Symbol的原因是改变量只能内部使用
const MyPromise = (() => {
//在这里定义内部变量
const PENDING = "pending"
const RESOLVED = "resolved"
const REJECTED = "rejected"
//使用Symbol是为了仅供内部使用
const PromiseStatus = Symbol("PromiseStatus")//当前状态
const PromiseValue = Symbol("PromiseValue")//当前值
return class {
//构造器
constructor(executor) {
//...
}
})()
我们都知道,当我们new Promise的时候,会有一个状态,会有值,还会传入两个函数,resolve和reject。那么我们实现一下:
constructor(executor) {
//初始化
this[PromiseStatus] = PENDING //当前状态
this[PromiseValue] = undefined //当前值
/**
* 定义 resolve 函数
* @param {*} data: 要返回的数据
*/
const resolve = (data) => {
//...
}
/**
* 定义reject函数
* @param {*} data: 要返回的数据
*/
const reject = (data) => {
//...
}
//执行
executor(resolve, reject)
}
接下来,我们就要实现resolve和reject函数的功能,其实这两个函数的功能非常简单,就是修改当前的状态和值,并且如果有任务队列,我们就执行任务队列中的内容。我把这两个函数的功能封装一下:
/**
* 改变状态的函数
* @param {*} data: 数据
* @param {*} status: 要改变的状态,resolve 或 reject
* @param {*} queue: 任务队列
*/
[changeStatus](data, status, queue) {
//如果已经是已决状态,那么直接结束
if (this[PromiseStatus] !== PENDING) return;
this[PromiseStatus] = status //修改当前状态
this[PromiseValue] = data //修改值
//变成已决阶段后,执行相应的队列函数
queue.forEach(q => q(data))
}
//---- 在 resolve 函数中调用
this[changeStatus](data, RESOLVED, this[thenables])
//---- 在 reject 函数中调用
this[changeStatus](data, REJECTED, this[catchables])
接下来我们要实现then和catch这两个后续处理函数,这两个函数实现什么功能想必大家都知道了,但我们要注意的是,如果调用then或者catch的时候,当前的状态已经是已决状态,那么就要直接执行。如果不是,那么就加入任务队列中,我在构造器中已经声明了 thenables 和 catchables 变量(then和catch函数的任务队列)
//settled then处理函数
then(thenable, catchable) {
//每个then都要返回一个新的promise
return this[linkPromise](thenable, catchable)
}
//settled catch处理函数
catch (catchable) {
return this[linkPromise](undefined, catchable)
}
因为功能相同,我进行了 2 次封装,都是哪两次封装呢?这是第一次,也就是实现加入任务队列的功能:
/**
* then和catch的处理函数,分为两种情况,如果当前已经是已决状态,
* 那么直接执行(此时直接执行也要加入事件队列中,无法模拟微队列,只能用宏队列实现下),如果当前还是未决状态,
* 那么把当前的处理函数加入相应的任务队列中
* @param {*} handler 处理函数
* @param {*} queue 处理队列
*/
[settledHandler](handler, status, queue) {
//如果不是函数,那么直接返回
if (typeof handler !== "function") return
if (this[PromiseStatus] === status) {
//如果已经是已决状态,直接执行
setTimeout(() => {
handler(this[PromiseValue])
}, 0);
} else {
//处于未决状态,加入任务队列
queue.push(handler)
}
}
还有一次封装,也就是我们上面调用的 linkPromise 函数,为什么要调用这个函数?因为then和catch后续处理函数,都要返回一个新的Promise,我们什么时候知道,then 或 catch 函数执行了,该返回新的Promise呢?这里是比较难的一部分,我们可以将 settledHandler 也就是执行处理函数的功能,反正创建一个新的 Promise中执行:
/**
* 用于创建一个新的Promise, 当我们调用then和catch处理函数时, 会返回一个新的Promise
* @param {*} thenable
* @param {*} catchable
*/
[linkPromise](thenable, catchable) {
/**
* 返回一个新的Promise的状态处理,如果父级已经变为已决状态, 那么新的Promise也是已决状态
* @param {*} data
* @param {*} handler
* @param {*} resolve
* @param {*} reject
*/
function exec(data, handler, resolve, reject) {
try {
//获取返回值
const res = handler(data)
//如果返回的是一个Promise,此时我们直接处理一下就可以
if (res instanceof MyPromise) {
res.then(data => resolve(data), err => reject(err))
} else {
//改变状态,和修改值
resolve(res)
}
} catch (error) {
reject(error)
}
}
//返回新的Promise
return new MyPromise((r

本文深入剖析Promise的工作原理,包括其内部状态管理、resolve/reject机制及then/catch方法的实现。同时,探讨Promise的静态成员如all、race、resolve和reject的使用,以及async/await语法糖的应用,帮助读者掌握异步编程的核心技巧。
最低0.47元/天 解锁文章
462





