通过前面我们学习了promise的基本使用,现在通过手写来实现一个promise。
1.promise的基本结构
ini
const PROMISE_PENDING="pending"
const PROMISE_FULFILLED="fulfilled"
const PROMISE_REJECTED="rejected"
class HYPromise {
constructor(executor){
this.status=PROMISE_PENDING;
this.value=undefined;
this.reason=undefined;
const resolve=(value)=>{
if(this.status===PROMISE_PENDING){
this.status=PROMISE_FULFILLED;
this.value=value;
console.log('fulfilled',value);
}
}
const reject=(reason)=>{
if(this.status===PROMISE_PENDING){
this.status=PROMISE_REJECTED;
this.reason=reason;
console.log('rejected',reason);
}
}
executor(resolve,reject)
}
}
const promise = new MYPromise((resolve,reject)=>{
resolve('111');
reject('222');
})
//'fulfilled',111
在这个代码中,我们先定义了一个HYPromise,然后通过new HYPromise创建一个新的promise。在创建的新的promise的时候他会立即执行constructor构造方法,用executor来接收传递过来的方法。
当代码运行时,首先会将此时的状态变为pending状态,然后判断是先执行了resolve方法还是reject方法,此段代码先执行的是resolve方法,即会调用HYPromise中的resolve,此时的状态就会变为fulfilled。但再进行下面的reject方法时,由于状态不是pending,则不会执行,从而实现了状态的简单管理。
2.promise的then方法实现
ini
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED;
queueMicrotask(() => {
this.value = value;
this.onFulfilled(this.value);
});
}
};
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED;
queueMicrotask(() => {
this.reason = reason;
this.onRejected(this.reason);
});
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled;
this.onRejected = onRejected;
}
}
const promise = new HYPromise((resolve, reject) => {
console.log('状态pending');
resolve(1111);
});
promise.then(
(res) => {
console.log('res1:', res);
},
(err) => {
console.log('err:', err);
}
);
//打印结果
//'res1',111
当创建的 HYPromise 实例调用 then 方法时,它会接收两个回调函数作为参数,并分别赋值给 this.onFulfilled 和 this.onRejected。
queueMicrotask 方法用于在当前事件循环的末尾执行一个微任务。在这个例子中,当 resolve 或 reject 被调用时,我们通过 queueMicrotask 将回调函数(onFulfilled 或 onRejected)的执行推迟到当前宏任务完成后。这样做的好处是,它允许我们在事件循环的当前迭代中,在可能的其他代码执行完毕后,再执行这些回调函数。如果没有queueMicrotask方法,则此代码会报错。
但是这个方法还存在一个问题,就是如果连续调用了then方法,则第一个then方法不会执行,因为保存的第二次保存的this.onFulfilled会覆盖第一次保存的this.onFulfilled。接下来将进行then()方法的优化。
3.promise的then方法的优化
kotlin
// ES6 ES2015
// https://promisesaplus.com/
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
const promise = new HYPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111)
// reject(2222)
// throw new Error("executor error message")
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
//打印结果
状态pending
res1: 1111
res2: aaaa
在这里我们创建了2个数组,一个是onFulfilledFns,即调用then()方法成功回调的集合,还有一个onRejectedFns即失败的回调。
此段代码通过new HYPromise,先执行了resolve(),然后因为有微任务函数queueMicrotask(),它会先执行宏任务函数,最后在执行微任务。后面又调用了then()方法,此时的status是还没有确定的,还是pending状态,然后就可以将回调函数保存在对应的数组中。当执行完这些then方法后,就执行微任务queueMicrotask函数,此时status为fulfilled,通过this.value = value保存resolve传来的参数,最后在foreach循环执行。
在这里我们的then()方法返回的是一个HYPromise,如果不是传的promise对象,在继续通过.then()方法会报undefined。
另外根据传统的promise,在调用reject()方法后,它会先打印一个err,然后调用then方法时,他的状态会变为pending.
这里的execFunctionWithCatchError,我们封装了一个方法,把promise的回调方法传过去(onFulfilled或者onRejected),然后传值,还有resolve,reject方法,这个execFunctionWithCatchError通过try-catch去拦截抛出异常,如果抛出了异常,就继续调用reject方法。
当多次调用then方法时,它就能完成正常promise调用。
4.promise-catch方法设计
kotlin
class HYPromise {
......
catch(onRejected) {
return this.then(undefined, onRejected)
}
}
这个catch方法我们可以直接通过调用then方法,然后传入一个reject方法。但是这个实现还存在一些问题,当调用catch,传入then方法时,因为onFulfilled是空的,所以会有报错。所以我们修改一下then方法。加入判空操作。
javascript
class HYPromise {
......
then(onFulfilled, onRejected) {
...
return new HYPromise((resolve, reject) => {
...
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled) this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
if (onRejected) this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
javascript
const promise = new HYPromise((resolve, reject) => {
console.log('状态pending');
// resolve(1111) // resolved/fulfilled
reject(2222);
});
promise
.then((res) => {
console.log('res:', res);
})
.catch((err) => {
console.log('err:', err);
});
当我们这样调用时,发现并没有打印err,没有调用catch方法。
因为调用的reject方法是通过传入then方法的第二个回调参数,但是这个then方法并没有传,即为undefined,所以就没有传入onRejectedFns中。而调用的catach方法是第二个的promise的catch方法,但是上一步的promise对象并没有被创建,因此不会执行。为了能直接执行catch方法,我们要在then方法在做修改。
ini
then(onFulfilled, onRejected) {
const defaultOnRejected = (err) => {
throw err;
};
onRejected = onRejected || defaultOnRejected;
}
在then方法开始的时候做一个判断,如果没有传递第二个参数,则抛出一个异常。则this.onRejectedFns数组里会生成一个函数,继续执行就会创建一个新的promise,然后再通过promise去调用catch方法。
5.promise-finally方法设计
scss
class HYPromise{
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
}
这个方法就是不管是成功还是失败,都会调用onFinally()回调函数的代码。
但是会存在一个问题,如果在这之前调用了catch方法。因为我们在上面的catch方法中传入then方法的第一个参数是undefined。因此也会存在调用undefined.finally()。因此我们需要在then方法中,再加一个判断返回传入的值。
ini
then(onFulfilled, onRejected) {
const defaultOnFulfilled = value => { return value }
onFulfilled = onFulfilled || defaultOnFulfilled
}
6.promise-resolve-reject类方法
在promise中,可以直接调用resolve和reject,即这是个静态方法。
javascript
class HYPromise{
static resolve(value) {
return new HYPromise((resolve) => resolve(value))
}
static reject(reason) {
return new HYPromise((resolve, reject) => reject(reason))
}
}
HYPromise.resolve("Hello World").then(res => {
console.log("res:", res)
})
HYPromise.reject("Error Message").catch(err => {
console.log("err:", err)
})
// 结果
res: Hello World
err: Error Message
7.promise-all-allSettled类方法
首先all和allSettled方法都是可以直接调用的,即都是promise的类方法。另外all方法接收一个 Promise 对象的数组作为参数,并返回一个新的 Promise 实例,这个新的 Promise 实例在数组中的所有 Promise 都成功解析时才会解析,其解析值为所有 Promise 解析值的数组。如果数组中的任何一个 Promise 被拒绝,则新的 Promise 会立即被拒绝,并返回err结果。allSteeled方法也是接收一个Promise数组,会执行全部方法,并且返回使用的status和value。
scss
class HYPromise{
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new HYPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push(res)
if (values.length === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
static allSettled(promises) {
return new HYPromise((resolve) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({ status: PROMISE_STATUS_FULFILLED, value: res})
if (results.length === promises.length) {
resolve(results)
}
}, err => {
results.push({ status: PROMISE_STATUS_REJECTED, value: err})
if (results.length === promises.length) {
resolve(results)
}
})
})
})
}
}
}
const p1 = new Promise((resolve) => {
setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve) => {
setTimeout(() => { resolve(3333) }, 3000)
})
// HYPromise.all([p1, p2, p3]).then(res => {
// console.log(res)
// }).catch(err => {
// console.log(err)
// })
HYPromise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
})
//all方法打印
2222
//allSettled打印
[ { status: 'fulfilled', value: 1111 }, { status: 'rejected', value: 2222 }, { status: 'fulfilled', value: 3333 }]
这里的all方法里这个判断if (values.length === promises.length) {resolve(values)};是假如全部的promise返回都为成功的话,就执行resolve。如果不是就遇到reject就返回错误信息。
8.promise-race-any类方法
race,any和all,allSettled都一样是类方法。
race是只要有一个Promise实例有结果了,不管是成功还是异常都直接进行打印。
any的话跟race也差不多只是它必须得有一个成功的结果才返回,如果全是错误它就会合并错误并返回。
javascript
class HYPromise{
...
static race(promises) {
return new HYPromise((resolve, reject) => {
promises.forEach(promise => {
// promise.then(res => {
// resolve(res)
// }, err => {
// reject(err)
// })
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new HYPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1111);
}, 3000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2222);
}, 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3333);
}, 3000);
});
// HYPromise.race([p1, p2, p3]).then(res => {
// console.log("res:", res)
// }).catch(err => {
// console.log("err:", err)
// })
HYPromise.any([p1, p2, p3])
.then((res) => {
console.log('res:', res);
})
.catch((err) => {
console.log('err:', err.errors);
});
//race方法打印
//any方法打印
res: 1111
(如果第一个是reject(1111))
err: [ 2222, 1111, 3333 ]
//race方法打印
err: 2222
到这里我们实现的基本差不多了,在promise中基本的方法都是围绕then方法的实现。
9.完整代码如下
scss
// ES6 ES2015
// https://promisesaplus.com/
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';
// 工具函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value);
resolve(result);
} catch (err) {
reject(err);
}
}
class HYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledFns = [];
this.onRejectedFns = [];
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return;
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onFulfilledFns.forEach((fn) => {
fn(this.value);
});
});
}
};
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return;
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onRejectedFns.forEach((fn) => {
fn(this.reason);
});
});
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
const defaultOnRejected = (err) => {
throw err;
};
onRejected = onRejected || defaultOnRejected;
const defaultOnFulfilled = (value) => {
return value;
};
onFulfilled = onFulfilled || defaultOnFulfilled;
return new HYPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled)
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(
onFulfilled,
this.value,
resolve,
reject
);
});
if (onRejected)
this.onRejectedFns.push(() => {
execFunctionWithCatchError(
onRejected,
this.reason,
resolve,
reject
);
});
}
});
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
finally(onFinally) {
this.then(
() => {
onFinally();
},
() => {
onFinally();
}
);
}
static resolve(value) {
return new HYPromise((resolve) => resolve(value));
}
static reject(reason) {
return new HYPromise((resolve, reject) => reject(reason));
}
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new HYPromise((resolve, reject) => {
const values = [];
promises.forEach((promise) => {
promise.then(
(res) => {
values.push(res);
if (values.length === promises.length) {
resolve(values);
}
},
(err) => {
reject(err);
}
);
});
});
}
static allSettled(promises) {
return new HYPromise((resolve) => {
const results = [];
promises.forEach((promise) => {
promise.then(
(res) => {
results.push({ status: PROMISE_STATUS_FULFILLED, value: res });
if (results.length === promises.length) {
resolve(results);
}
},
(err) => {
results.push({ status: PROMISE_STATUS_REJECTED, value: err });
if (results.length === promises.length) {
resolve(results);
}
}
);
});
});
}
static race(promises) {
return new HYPromise((resolve, reject) => {
promises.forEach((promise) => {
// promise.then(res => {
// resolve(res)
// }, err => {
// reject(err)
// })
promise.then(resolve, reject);
});
});
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = [];
return new HYPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, (err) => {
reasons.push(err);
if (reasons.length === promises.length) {
reject(new AggregateError(reasons));
}
});
});
});
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1111);
}, 3000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2222);
}, 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3333);
}, 3000);
});
HYPromise.race([p1, p2, p3])
.then((res) => {
console.log('res:', res);
})
.catch((err) => {
console.log('err:', err);
});
// HYPromise.any([p1, p2, p3])
// .then((res) => {
// console.log('res:', res);
// })
// .catch((err) => {
// console.log('err:', err.errors);
// });