ES6之手写Promise
接下来,将一步步手写一个 Promise ,将实现一个 Promise 的基本功能。
const PENDING='pending';
const FULFILLED='fulfilled';
const REJECTED='rejected';
class MyPromise {
/**
*
* @param {Function} executor 构造器,创建一个Promise对象
*/
constructor(executor) {
this._state = PENDING;
this._value = undefined;
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
}
}
/**
*
* @param {String} newState 改变状态
* @param {any} value 改变相关数据
* @returns
*/
_changeState(newState, value) {
if (this._state !== PENDING) {
return;
}
this._state = newState;
this._value = value;
}
/**
*
* @param {any} data 成功数据
* @returns
*/
_resolve = (data) => this._changeState(FULFILLED, data)
/**
*
* @param {any} reason 失败数据
* @returns
*/
_reject = (reason) => this._changeState(REJECTED, reason)
}
const myP = new MyPromise((resolve, reject) => {
throw new Error('出错了');
resolve('洗完澡了');
resolve('喝完水了')
})
console.log(myP)
创建一个 MyPromise 的类,构造器中将状态和相关数据放进去。因为 resolve 和 reject 就是改变状态和数据,所以创建一个改变状态和数据的辅助函数 changeState ,状态确定之后不可改变,所以在 changeState 中要判断后面调用不会再进行赋值。同时,如果报错也会直接调用 reject 函数。
/**
* 运行微任务,将传递来的函数放到微队列之中
* @param {Function} callback
*/
function runMicroTask(callback) {
if (process && process.nextTick) {
process.nextTick(callback);
} else if (MutationObserver) {
//只要内部元素一变化,它将一个回调函数放入微队列中
const p = document.createElement('p');
const observer = new MutationObserver(callback);
observer.observe(p, {
childList: true
});
p.innerHTML = '1';
} else {
setTimeout(callback, 0);
}
}
这个辅助函数实现了将一个回调函数放到微队列之中。可以分为 node 环境和浏览器环境。node 环境使用 nextTick 就可以了,浏览器环境需要建立一个观察器,观察元素内部的变化,内部一变化,MutationObserver 就将一个回调函数放到微队列中。如果还有其他的情况,就将回调函数放到计时器中。
/**
* 处理队列中加一个then相关的对象
* @param {Function} executor then执行的函数
* @param {String} state 执行时要求的状态
* @param {Function} resolve 执行的成功回调
* @param {Function} reject 执行的失败回调
*/
_pushHandler(executor, state, resolve, reject) {
this._handlers.push({ executor, state, resolve, reject });
}
/**
*
* @param {Function} onFulfilled 成功的回调
* @param {Function} onRejected 失败的回调
* @returns
*/
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
this._pushHandler(onRejected, REJECTED, resolve, reject);
})
}
我们先把要执行的 then 函数放到一个队列中,在执行时再依次执行。创建一个辅助函数 pushHandler ,将函数,状态,成功和失败的函数回调。在执行 then 函数时,将一个对象传进去。
/**
* 执行队列
*/
_runHandlers() {
if (this._state === PENDING) {
return;
}
while (this._handlers[0]) {
const handler = this._handlers[0];
this._runOneHandler(handler)
this._handlers.shift();
}
}
前面调用 then 的时候会放到一个队列里,这个函数开始遍历每个元素,然后放到处理单个 handler 的函数中,注意:使用 shift() 删除第一个元素的时候,要使用 while 循环,使用 for 循环会出现指向错误。在状态变化的时候和 then 方法的时候都需要调用。
/**
* 判断一个对象是否是Promise对象
* @param {any} obj
* @returns
*/
function isPromise(obj) {
return !!(obj && typeof obj === 'object' && typeof obj.then === 'function');
}
/**
* 执行一个队列
* @param {Object} handler 队列对象
*/
_runOneHandler({ executor, state, resolve, reject }) {
runMicroTask(() => {
if (this._state !== state) {
//状态不一致
return;
}
if (typeof executor !== 'function') {
//then里面传递的不是函数类型
this._state === FULFILLED ? resolve(this._value) : reject(this._value);
return;
}
try {
//函数返回的结果
const result = executor(this._value);
console.log(result);
if (isPromise(result)) {
return result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
console.error(error);
}
});
}
现在要把每一个对象放到微队列里面。先要判断状态是否一致,不一致跳过,判断是否是一个函数,不是函数会发生状态穿透,保持原来的状态和数据。当 handler 在执行的时候,如果没有报错,就像返回的数据作为成功的结果,如果报错,就作为失败的原因。如果返回的是一个 Promise ,满足 A+ 规范,则和这个 Promise 保持一致,它成功最后就成功,反之亦然。