手写Promise,已经成为大厂面试99.9%必考的一道面试题了。
写代码前,首先搞明白promise的原理及用法,知道了这些,那么每一个步骤实现起来就不会摸不着头脑了。
- Promise其实就是一个类, 构造函数,内部保存着回调队列,通过暴露resolve和reject方法触发对应回调
- promise有三种状态值:pending(等待),fulfiled(成功),failed(失败)
- 常用方法以下几种:then, catch, all, race, resolve, reject
- 一旦实例了一个Promise,其就一定会执行,无法停止,Promise中,由于报错也会触发reject,所以不会中断程序
- catch其实是then的语法糖,相当于then(null,rej),即成功回调传了空
- 如果then中,没有传入回调,则promise的resolve或reject值会传递到下一个then或catch中
Promise 构造函数
function myPromise(func) {
this.status = 'pending'; // 状态
this.value = null; // 值
this.resolveList = []; // 成功回调
this.rejectList = []; // 失败回调
const resolve = (data) => {
if (this.status === 'pending') {
this.status = 'fulfiled';
this.value = data;
this.resolveList.forEach((resFunc) => resFunc(data));
this.resolveList.length = 0;
}
};
const reject = (data) => {
if (this.status === 'pending') {
this.status = 'failed';
this.value = data;
this.rejectList.forEach((resFunc) => resFunc(data));
this.rejectList.length = 0;
}
};
if (typeof func === 'function') {
try {
func(resolve, reject); // promise传入的func是马上执行的,但回调resolve和reject是异步微任务
} catch(err) {
reject(err);
}
}
}
then()
then方法定义在原型链上,返回一个promise, pending时将回调 添加到回调队列中,同时Promise化,fulfilled或failed时直接执行,并返回promise
myPromise.prototype.then = function (resCb, rejCb) {
if (this.status === 'pending') {
return new myPromise((res, rej) => {
this.resolveList.push(() => {
handleResolve(res, rej, resCb, rejCb, this.value)
});
this.rejectList.push(() => {
handleReject(res, rej, resCb, rejCb, this.value)
})
})
} else if (this.status === 'fulfiled') {
return new myPromise((res, rej) => {
handleResolve(res, rej, resCb, rejCb, this.value)
})
} else if (this.status === 'failed') {
return new myPromise((res, rej) => {
handleReject(res, rej, resCb, rejCb, this.value)
})
}
}
function handleResolve(res, rej, resCb, rejCb, value) {
queueMicrotask(() => {
// 如果没有定义回调,将值传递给下一个
try {
const ret = typeof resCb === 'function' ? resCb(value) : value;
if (ret instanceof myPromise) {
ret.then(res, rej);
} else {
res(ret)
}
} catch (err) {
rej(err);
}
});
}
function handleReject(res, rej, resCb, rejCb, value) {
queueMicrotask(() => {
// 如果没有定义回调,将值传递给下一个
try {
const ret = typeof rejCb === 'function' ? rejCb(value) : value
if (ret instanceof myPromise) {
ret.then(res, rej);
} else {
rej(ret);
}
} catch (err) {
rej(err);
}
});
}
catch()
myPromise.prototype.then(null, rej) 的语法糖
myPromise.prototype.catch = function(rej){
return myPromise.prototype.then.call(this, null, rej)
}
all()
传入一个数组,输出一个promise,全部resolve时,输出promise状态变为fulfiled,触发resolve 当任何一个传入的promise失败时,输出promise状态变failed,触发reject, 但并不会终止其他promise继续执行,只是其他promise的执行已经不会影响promise.all返回promise的状态了
myPromise.all = function(promiseArr){
const len = promiseArr.length
const result = []
return new myPromise((resolve, reject) => {
let index = 0
promiseArr.forEach((item) => {
item.then((data) => {
result.push(data)
index++
if (index === len) resolve(result)
}, (err) => {
reject(err)
})
})
})
}
race()
返回promise数组中第一个执行完毕的状态,无论是fulfiled还是failed,但不影响数组中其余promise继续执行
myPromise.race = function(promiseArr) {
return new myPromise(function(resolve, reject){
promiseArr.forEach(item => {
item.then(data => {
resolve(data)
}, err=> {
reject(err)
})
})
})
}
resolve()
将现有数据转换为Promise,状态为resolved
myPromise.resolve = function(data){
return new myPromise((resolve, reject) => {
resolve(data)
})
};
reject()
myPromise.reject = (data) => {
return new myPromise((resolve, reject) => {
reject(data);
});
};
finally()
无论最后promise状态如何都会执行
myPromise.prototype.finally = function (callback){
let p = this.constructor
return this.then(
value => p.resolve(callback()).then(() => value),
reason => p.resolve(callback()).then(() => {throw reason})
)
}
done()
处于回调链的末端,能捕获到任何可能出现的错误
myPromise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected).catch(function(reason){
setTimeout(function(){
throw reason
}, 0)
})
}