参考资料
前言
Promise 规范有很多,如 Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+,最终 ES6 中采用了 [Promise/A+ 规范](Promise/A+ 规范)。
本文是对参考资料1中给出的源代码的阅读笔记,为了尊重原作者,建议还是点击上面链接查看原作者的文章。
另外,我还根据自己的理解,修改了一下代码,因为我觉得执行onRulfilled和onRjected的代码可以抽离出来单独作为单独的函数(因为多处存在复用)。我的修改版本也能通过Promises/A+ Compliance Test Suite。
Promise.constructor()
阅读Promise()源码,我认为从构造函数开始比较合适。
constructor(executor) {
this.state = PROMISE_STATES.PENDING; // 初始化状态,为PENDING
this.fulfillQueue = []; // 当前Promise实例状态为fulfilled时执行的回调
this.rejectQueue = []; // 当前Promise实例状态为rejected时执行的回调
// resolve.bind(this)返回一个resolve函数的副本,执行该resolve时里面
// 的this指向的是当前Promise实例。reject.bind(this)同理。
executor(resolve.bind(this), reject.bind(this))
}
Promise.then()
由于执行executor(resolve.bind(this), reject.bind(this))时可能会确定Promise的状态,确定状态时会执行通过then()注册的回调函数,因此还是先分析then()比较合适。
then(onFulfilled, onRejected) {
// 因为then()会返回一个新的Promise对象,因此这里创建promise2
const promise2 = new MyPromise(() => { })
/**
* 该分支的意思是:如果promise当前是fulfilled状态,
* 则直接执行传入的onFulfilled函数。
*
* 如果不使用nextTick(),executeOnFulfilled会直接执行resolvePromiseWithValue()
* 或transition(),这两个方法里会去执行通过then()注册的回调。而此时是还没
* 有在当前Promise实例上调用then()的,因为后面的then()代码还没执行。使用nextTick()
* 后,会先继续执行js执行栈剩余的代码,里面就包括了通过then()注册回调的代码。
*/
if (this.state === PROMISE_STATES.FULFILLED) {
nextTick(() => { executeOnFulfilled(onFulfilled, this.value, promise2) })
}
/**
* 该分支的意思是:如果promise当前是rejected状态,
* 则直接执行传入的onRejected函数。
*/
else if (this.state === PROMISE_STATES.REJECTED) {
nextTick(() => { executeOnRejected(onRejected, this.reason, promise2) })
}
/**
* 该分支的意思是:如果promise当前是pending状态,
* 则将onFulfilled函数和onRejected函数分别放到对应的队列中去。
*/
else if (this.state === PROMISE_STATES.PENDING) {
this.fulfillQueue.push({ handler: onFulfilled, chainedPromise: promise2 })
this.rejectQueue.push({ handler: onRejected, chainedPromise: promise2 })
}
return promise2;
}
resolve() & reject()
/**
* 因为resolve得到的是一个js逻辑值,而不同的值会有不同的处理方案,因此
* 调用resolvePromiseWithValue()
*/
function resolve(value) {
resolvePromiseWithValue(this, value)
}
/**
* 因为reject得到的是一个异常,直接进入rejected,然后报错即可
*/
function reject(reason) {
transition(this, PROMISE_STATES.REJECTED, reason)
}
resolvePromiseWithValue()
/**
*
* @param {*} promise
* @param {*} x 取值可能有4种情况:Promise实例、thenable对象、普通对象(包括函数)、其他
* @param {*} thenableValues 用于检测thenable cycle
*/
function resolvePromiseWithValue(promise, x, thenableValues = []) {
// 如果promise和x执行同一个对象,则报错
if (promise === x) {
transition(promise, PROMISE_STATES.REJECTED, new TypeError('promise and x cannot refer to the same object.'))
}
/**
* 这个分支的意思是:如果promise决议时收到的值是一个Promise实例,
* 那么x的在确定状态后,会将其状态和值传给promise。
*/
else if (isPromise(x)) {
// 如果x已经确定了状态,则直接用transition转移promise的状态
if (x.state !== PROMISE_STATES.PENDING) {
transition(promise, x.state, x.state === PROMISE_STATES.FULFILLED ? x.value : x.reason)
}
// 如果x的状态还是PENDING,则等x确定状态后,再做处理
else {
x.then(value => {
resolvePromiseWithValue(promise, value, thenableValues)
}, reason => {
transition(promise, PROMISE_STATES.REJECTED, reason)
})
}
}
/**
* 这个分支的意思是:如果promise决议时收到的值x是一个对象或方法,
* 则执行x的then(),并将值传给promise。
* 于此同时,会检查是否出现thenable cycle。thenable cycle指的是
* 如果类Promise对象的then()中传给resolve()的值也是一个类Promise
* 对象,并且个新的类Promise对象的then()传给resolve()的值也是一个
* 类Promise对象...,后来其中一个类Promise对象的then()传给resolve()
* 的类Promise对象在之前出现过,那么就产生了一个“环”。
*/
else if (isObject(x) || typeof x === 'function') {
let isInvoked = false; // 保证只执行一次(我没看懂是什么只执行一次)
try {
const then = x.then;
// 如果x.then是一个函数,则认为x是一个类Promise对象(鸭式辩型)
if (typeof then === 'function') {
// 这里的操作我没看懂,为啥不用x.then(value=>{}, reason=>{})
then.call(x, value => {
// 如果出现了环,则转为rejected
if (thenableValues.indexOf(value) !== -1) {
transition(promise, PROMISE_STATES.REJECTED, new TypeError('there is a thenable cycle that will lead to infinite recursion.'))
}
if (!isInvoked) {
thenableValues.push(value)
resolvePromiseWithValue(promise, value, thenableValues)
isInvoked = true;
}
}, reason => {
if (!isInvoked) {
transition(promise, PROMISE_STATES.REJECTED, reason)
isInvoked = true;
}
})
}
// 如果x.then不是一个函数,则x不是类Promise对象,promise转为fulfilled,值就是x
else {
transition(promise, PROMISE_STATES.FULFILLED, x)
}
} catch (error) {
if (!isInvoked) {
transition(promise, PROMISE_STATES.REJECTED, error)
}
}
}
/**
* 这个分支的意思是,如果上面情况都不符合的话,promise转为fulfilled,
* 并且x作为fulfilled的value。
*/
else {
transition(promise, PROMISE_STATES.FULFILLED, x)
}
}
transition()
/**
* 状态转移
* @param {*} promise 待转移状态的promise
* @param {*} targetState 目标状态
* @param {*} value 转移时附带的值,可能是fulfilled的value,也可能是rejected的reason
* @returns
*/
function transition(promise, targetState, value) {
// 如果当前状态不是PENDING,或者目标状态是PENDING,则直接返回
if (promise.state !== PROMISE_STATES.PENDING || targetState === PROMISE_STATES.PENDING) {
return
}
// 状态转义后,state将不发生改变
Object.defineProperty(promise, 'state', {
configurable: false,
writable: false,
enumerable: true,
value: targetState
})
/**
* 这个分支的意思是:如果目标状态是FULFILLED,则确定value值,
* 并在下一个循环执行fulfillQueue上的所有handler。
*/
if (targetState === PROMISE_STATES.FULFILLED) {
Object.defineProperty(promise, 'value', {
configurable: false,
writable: false,
enumerable: true,
value
})
nextTick(() => {
promise.fulfillQueue.forEach(({ handler, chainedPromise }) => {
executeOnFulfilled(handler, value, chainedPromise)
})
promise.fulfillQueue = [];
})
}
/**
* 这个分支的意思是:如果目标状态是REJECTED,则确定value值,
* 并在下一个循环执行rejectQueue上的所有handler。
*/
else if (targetState === PROMISE_STATES.REJECTED) {
Object.defineProperty(promise, 'reason', {
configurable: false,
writable: false,
enumerable: true,
value
})
nextTick(() => {
promise.rejectQueue.forEach(({ handler, chainedPromise }) => {
executeOnRejected(handler, value, chainedPromise)
})
promise.rejectQueue = [];
})
}
}
executeOnFulfilled() & executeOnRejected()
// 执行onFulfilled回调
function executeOnFulfilled(onFulfilled, value, nextPromise) {
try {
/**
* 如果传入的onFulfilled是函数,则执行得到返回结果,然后调用
* resolvePromiseWithValue(),该方法会根据不同的值执行不同的处理方案。
*/
if (typeof onFulfilled === 'function') {
const adoptedValue = onFulfilled(value)
resolvePromiseWithValue(nextPromise, adoptedValue)
}
/**
* 如果传入的onFulfilled不是函数,则忽略,将当前Promise实例的状态和
* 值传给下一个Promise实例。
*/
else {
transition(nextPromise, PROMISE_STATES.FULFILLED, value)
}
}
/**
* 如果发生了异常,则转为rejected状态
*/
catch (error) {
transition(nextPromise, PROMISE_STATES.REJECTED, error)
}
}
// 执行onRejected回调
function executeOnRejected(onRejected, reason, nextPromise) {
// 该函数的注释参考executeOnFulfilled()
try {
if (typeof onRejected === 'function') {
const adoptedValue = onRejected(reason)
resolvePromiseWithValue(nextPromise, adoptedValue)
} else {
transition(nextPromise, PROMISE_STATES.REJECTED, reason)
}
} catch (error) {
transition(nextPromise, PROMISE_STATES.REJECTED, error)
}
}
nextTick()
// 添加微任务
function nextTick(callback) {
// 如果是node环境,则通过nextTick()实现添加微任务
if (typeof process !== 'undefined' && typeof process.nextTick === 'function') {
process.nextTick(callback)
}
// 如果是浏览器环境,则通过MutationObserver添加微任务
else {
const observer = new MutationObserver(callback)
const textNode = document.createTextNode('1')
observer.observe(textNode, {
characterData: true
})
textNode.data = '2'
}
}
isObject() & isPromise()
// 判断是否是Object实例
function isObject(val) {
return val && typeof val === 'object'
}
// 判断是否是Promise实例
function isPromise(val) {
return val instanceof MyPromise
}
为什么Promise要产生微任务
从上面的代码中可以看出,如果只有在执行onFulfilled回调或onRejected回调时才会用到nextTick(),这样的原因是,想把回调的执行放到下一个事件循环。如果不使用nextTick的话,在resolve()执行时,就会直接去执行onFulfilled回调,而由于主线程是单线程的,后面的then()代码还没执行到,回调还没注册,因此就执行不到任何回调。而通过nextTick()将回调的执行放到下一个事件循环后,执行栈的代码可以继续执行,then()处的代码就能执行到,从而在promise上注册回调。
let promise = new Promise((resolve, reject) => {
resolve(100)
})
.then((value) => {
console.log(value)
})