前言
Promise实现以同步的方式书写异步代码,解决了回调地狱的问题。本质上,Promise是观察者模式的一种实现,通过任务注册和状态监听优化了原来的编程模式,增加了代码的可读性和维护性。
手写一遍Promise可以加深我们对于Promise的理解,Promise的核心包括:3个属性,1个构造器、2个实例方法和4个静态方法。
类初始化
首先定义两个属性来保存Promise的状态和结果。
class Promise {
constructor(executor) {
this.PromiseState = 'pending' // 存储promise状态
this.PromiseResult = null // 存储promise结果
}
}
执行器函数
当实例化Promise时,用户需要传入一个执行器函数executor,执行器函数接收两个内部函数用于改变Promise的状态,所以这一步我们需要:
- 定义内部函数:resolve, reject 修改promise状态并保存值。
- 调用执行器函数:executor为同步执行,如果内部抛出异常则promise状态为rejected。
class Promise {
constructor(executor) {
this.PromiseState = 'pending'; // 存储promise状态
this.PromiseResult = null; // 存储promise结果
// 修改promise状态为成功
const resolve = (data) => {
// 如果状态已经改变,则不再改变
if (this.PromiseState !== 'pending') return;
// 如果状态未改变则改变状态并保存值
this.PromiseState = 'fulfilled';
this.PromiseResult = data;
}
// 修改promise状态为失败
const reject = (data) => {
// 如果状态已经改变,则不再改变
if (this.PromiseState !== 'pending') return;
// 如果状态未改变则改变状态并保存值
this.PromiseState = 'rejected';
this.PromiseResult = data;
}
// 执行器函数为同步执行
// 如果执行过程中抛出异常则promise状态为失败
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
then方法
接下来我们需要定义then方法,用户通过这个方法注册回调函数,当Promise状态改变时,回调将被执行。
- then接收两个回调函数作为参数,我们需要校验onResolved,onRejected是否为函数。
- then方法返回一个promise,并根据onResolved,onRejected执行结果调整promise状态。
- then方法中onResolved,onRejected函数为异步执行。
- 如果then执行时promise的状态还未改变,则保存回调函数等待执行,这里需要添加第三个属性callbacks保存then中注册的回调方法。
class Promise {
constructor(executor) {
this.PromiseState = 'pending'; // 存储promise状态
this.PromiseResult = null; // 存储promise结果
this.callbacks = []; // 保存then中添加的回调方法
// 修改promise状态为成功
const resolve = (data) => {
// 如果状态已经改变,则不再改变
if (this.PromiseState !== 'pending') return;
// 如果状态未改变则改变状态并保存值
this.PromiseState = 'fulfilled';
this.PromiseResult = data;
// 执行then中保存下来的回调
this.callbacks.forEach((item) => {
item.onResolved();
});
};
// 修改promise状态为失败
const reject = (data) => {
// 如果状态已经改变,则不再改变
if (this.PromiseState !== 'pending') return;
// 如果状态未改变则改变状态并保存值
this.PromiseState = 'rejected';
this.PromiseResult = data;
// 执行then中保存下来的回调
this.callbacks.forEach((item) => {
item.onRejected();
});
};
// 执行器函数为同步执行
// 如果执行过程中抛出异常则promise状态为失败
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then方法
then(onResolved, onRejected) {
// 校验onResolved,onRejected是否为函数
if (typeof onResolved !== 'function') {
onResolved = value => value
}
if (typeof onRejected !== 'function') {
onRejected = error => {
throw error
}
}
// then的返回值为一个Promise
return new Promise((resolve, reject) => {
// then返回的Promise状态需要根据onResolved和onRejected的返回结果而定
const callback = (func) => {
// 捕获函数执行时抛出的异常(由于异步执行,executor处的trycatch无法捕获)
try {
const result = func(this.PromiseResult);
if (result instanceof Promise) {
result.then(
(value) => {
resolve(value);
},
(error) => {
reject(error);
}
);
} else {
resolve(result);
}
} catch(error) {
reject(error);
}
};
// 如果promise状态已经改变,则异步执行回调
// 真实情况下会将回调加入微任务队列,这里用宏任务模拟
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
});
}
// 如果promise状态还未改变,则先保存
// 这些方法会在执行器函数中resolve或reject方法执行时执行
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: () => {
setTimeout(() => {
callback(onResolved);
});
},
onRejected: () => {
setTimeout(() => {
callback(onRejected);
});
},
});
}
});
}
}
catch方法
catch方法是then方法的语法糖,我们定义一下。
// catch方法(这儿还是定义在Promise类内部,冗余代码过多,后面就不写了)
catch(onRejected) {
return this.then(undefined, onRejected);
}
resolve方法
接下来定义几个静态方法,首先是resolve,这个方法用于将数据包装为Promise,如果传入的数据是Promise则直接返回,否则返回一个将入参作为结果的成功状态的Promise
// resolve方法
static resolve(data) {
// 如果传入Promise则直接返回
if (data instanceof Promise) {
return data;
} else {
return new Promise((resolve) => {
resolve(data);
});
}
}
reject方法
再来是reject,该方法不论入参是不是Promise,均返回一个失败的Promise,并且值就是传入的数据
// reject 方法
static reject(data) {
return new Promise((_, reject) => {
reject(data);
});
}
all方法
接下来是all,all方法用于判断多个promise是否均为成功,如果多个promise都成功则返回所有传入promise的返回值数组;如果有一个promise失败,则返回该失败的promise的返回值
// all方法
static all(promises) {
let count = 0;
let arr = [];
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(value) => {
count++;
arr[i] = value;
if (count === promises.length) {
resolve(arr);
}
},
(error) => {
reject(error);
}
);
}
});
}
race方法
最后是race,该方法用于多个promise竞争执行,如果其中一个promise成功,则返回该成功promise的返回值;如果其中一个promise失败,则返回该失败promise的返回值
// race方法
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(value) => {
resolve(value);
},
(error) => {
reject(error);
}
);
}
});
}