手写Promise
nys013 原理面试
# 前言
手写了个MyPromise
类实现了基本的Promise
功能,包括promise
的创建、链式回调、then
、catch
、Promise.resolve
、Promise.reject
、Promise.all
、Promise.race
。
主要看代码和注释吧
# 定义类和构造器
Promise
的三种状态:pending
进行中、fulfilled
已完成、rejected
已拒绝,用status
存储Promise
的执行器函数的传参放放于value
中,用于异步操作后返回的结果。- 两个队列分别存储成功和失败的回调函数,用于异步操作后取出执行回调。
class MyPromise {
// 定义Promise的三种状态
static PENDING = 'pending'; // 进行中
static FULFILLED = 'fulfilled'; // 已完成
static REJECTED = 'rejected'; // 已拒绝
value = undefined;
status = MyPromise.PENDING;
onFulFilledCallbacks = []; // 成功回调队列
onRejectedCallbacks = []; // 失败回调队列
/**
* 构造函数,接受一个执行器函数作为参数,用于异步操作。
* @param {Function} executor 执行器函数,接受resolve和reject两个参数。
*/
constructor(executor) {
const resolve = value => {
// 当状态为PENDING时,改变状态为FULFILLED,取出成功回调队列一一执行
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
this.onFulFilledCallbacks.forEach(fn => fn(value));
}
};
const reject = value => {
// 当状态为PENDING时,改变状态为REJECTED,取出失败回调队列一一执行
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value;
this.onRejectedCallbacks.forEach(fn => fn(value));
}
};
try {
executor(resolve, reject); // 执行传入的执行器函数
} catch (error) {
reject(error); // 如果执行器函数抛出错误,则调用reject
}
}
}
# then
promise
的关键then
方法,接收两个回调函数,去处理promise
中同步或异步操作的结果
为了能够链式调用,then
返回的也会是一个promise
queueMicrotask (opens new window)接收一个回调,让回调在微任务队列中执行。看到一些封装用的是setTimeout
,但是setTimeout
是宏任务,这样执行顺序会和promise有些区别了。
/**
* then方法用于注册成功和失败的回调函数。
* @param {Function} onFulFilled 成功回调函数
* @param {Function} onRejected 失败回调函数
* @returns {MyPromise} 返回一个新的MyPromise实例
*/
then(onFulFilled, onRejected){
// 如果传参不是函数,则封装成函数 返回其默认值/抛出异常
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : (value) => value;
onRejected = typeof onRejected === 'function' ? onRejected : (value) => {throw value};
return new MyPromise((resolve, reject)=>{ // 返回一个新的MyPromise实例
if (this.status === MyPromise.PENDING) {
// 当状态为PENDING时,说明执行器函数中传入的resolve/reject函数还未执行,即是函数是异步调用
// 将成功和失败的回调分别放入前面定义的回调队列中,等待异步任务执行到resolve/reject函数调用后,上面定义的resolve/reject函数会将回调队列取出执行
this.onFulFilledCallbacks.push(() => {
queueMicrotask(() => {
try {
const result = onFulFilled(this.value);
this.handlePromiseResult(result, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
const result = onRejected(this.value);
this.handlePromiseResult(result, resolve, reject);
} catch (error) {
reject(error);
}
});
});
} else if (this.status === MyPromise.FULFILLED) {
// 当状态为FULFILLED时,说明resolve已经执行,则执行成功回调
queueMicrotask(() => {
try {
const result = onFulFilled(this.value);
this.handlePromiseResult(result, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === MyPromise.REJECTED) {
// 当状态为REJECTED时,执行失败回调
queueMicrotask(() => {
try {
const result = onRejected(this.value);
this.handlePromiseResult(result, resolve, reject);
} catch (error) {
reject(error);
}
});
}
});
}
promise
是能进行链式调用的,所有需要对回调的返回值做一些处理,
/**
* 处理promise链式调用的结果。
* @param {any} result then返回的结果
* @param {Function} resolve 解决新promise的方法
* @param {Function} reject 拒绝新promise的方法
*/
handlePromiseResult(result, resolve, reject){
if (result instanceof MyPromise) {
// 如果结果是MyPromise实例,则递归处理
result.then(resolve, reject);
} else {
// 否则,直接解决新promise
resolve(result);
}
}
# catch
用于注册失败的回调函数,其实就是then
方法的语法糖
/**
* catch方法用于注册失败的回调函数
* @param {Function} onRejected 失败回调函数
* @returns {MyPromise} 返回一个新的MyPromise实例
*/
catch(onRejected){
return this.then(null, onRejected);
}
# Promise.resolve
/**
* 静态方法,创建一个已解决的promise。
* @param {any} value 解决的值
* @returns {MyPromise} 返回一个新的已解决的MyPromise实例
*/
static resolve(value){
return new MyPromise(resolve => {
resolve(value);
});
}
# Promise.reject
/**
* 静态方法,创建一个已拒绝的promise。
* @param {any} value 拒绝的值
* @returns {MyPromise} 返回一个新的已拒绝的MyPromise实例
*/
static reject(value){
return new MyPromise((_, reject) => {
reject(value);
});
}
# Promise.all
有个点需要注意的是,返回值的顺序需和传入的promise
的顺序一致,而不是执行顺序,所以需要借助count
去记录完成的数量,而不能直接用index
来判断
/**
* 静态方法,接收一个promise数组,当所有promise都解决时,返回一个解决的promise。
* @param {Array} promiseArr promise数组
* @returns {MyPromise} 返回一个新的MyPromise实例,当所有输入的promise都解决时,它将被解决
*/
static all(promiseArr){
return new MyPromise((resolve, reject) => {
let result = []; // 存储解决的值
let count = 0; // 记录已解决的promise数量
promiseArr.forEach((promise, index) => {
promise.then(res => {
result[index] = res;
count++;
if (count === promiseArr.length) {
resolve(result);
}
}).catch(err => {
reject(err);
});
});
});
}
# Promise.race
/**
* 静态方法,接收一个promise数组,当任意一个promise解决或拒绝时,返回结果。
* @param {Array} promiseArr promise数组
* @returns {MyPromise} 返回一个新的MyPromise实例,当任意一个输入的promise解决或拒绝时,它将提供相同的结果
*/
static race(promiseArr){
return new MyPromise((resolve, reject) => {
promiseArr.forEach(promise => {
promise.then(res => {
resolve(res);
}).catch(err => {
reject(err);
});
});
});
}