♠ 手写简单promise
♣ 手写简单Promise 说明
Promise 规范
promise a+ 链接https://promisesaplus.com/,不过像 catch方法并没有参照这个规范,而是采用原生的promise方案
Promise 类设计
起一个类名JSPromise, class JSPromise {}
构造函数的规划
- 在new JSPromise,执行回调函数的时候,处于pending状态,再执行resolve状态变为fulfilled,如果同时执行resolve和reject,要考虑后面一个一定是无效的调用,可以用常量来控制,记录当前的promise是什么状态,可以参看后续的代码。
- 定义resolve和reject回调。
- 调用resolve的参数要保存起来(this.value = value),这样后续在 使用 then 函数的时候,才能拿到这个参数,包括reject的reason
- 执行resolve中,要执行then传入的成功回调, 所以也要在then方法中保存成功和失败的回调,如果直接在resolve 中执行成功的回调,显然是不行的,因为new的时候,首先执行构造函数,我们可以考虑使用微任务,先执行主线程,再去处理微任务,关于微任务和宏任务,进程,线程这些,我们单独在事件循环中讨论
- 考虑抛出异常(try....catch),这部分在reject讨论吧
下面是代码,不过有很多问题,我们一步一步来
点击查看代码
class JSPromise {
constructor(executor) {
// 定义状态
// 定义resolve、reject回调
// resolve执行微任务队列:改变状态、获取value、then传入执行成功回调
// reject执行微任务队列:改变状态、获取reason、then传入执行失败回调
// try catch
executor(resolve, reject);
}
}
点击查看代码
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class JSPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilled(this.value)
});
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejected(this.reason)
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const promise = new JSPromise((resolve, reject) => {
console.log("状态pending")
reject(2222)
resolve(1111)
})
// 调用then方法
promise.then(res => {
console.log("res1:", res)
//return 1111
}, err => {
console.log("err:", err)
})
then 方法的实现
点击查看代码
class JSPromise {
then(onFulfilled, onRejected) {
// this.onFulfilled = onFulfilled
// this.onRejected = onRejected
// 1.判断onFulfilled、onRejected,会给默认值
// 2.返回Promise resolve/reject
// 3.判断之前的promise状态是否确定
// onFulfilled/onRejected直接执行(捕获异常)
// 4.添加到数组中push(() => { 执行 onFulfilled/onRejected 直接执行代码})
}
}
要考虑的几个问题:
- 在上面的构造函数规划中,如果new 一次的实例,多次调用then(),后面的then方法中的成功和错误就会把前面的覆盖,解决这个问题我们可以使用数组存起来
- 如果是定时中执行then, 在原生promise中也是可以的,但是上面的实现是不行的,定时是宏任务,这时候微任务已经执行完了,状态已经是fulfilled,所以这种情况执行then就不能再push到数组,直接执行成功或者失败的回调就可以了
- 同时执行resolve和reject,这两个函数的微任务都会加入到队列中,依次执行内部的代码,我们可以在微任务中增加限制🚫,只有是pending状态才可以执行微任务中的回调
- 链式调用,原生执行then方法后,返回一个新的promise, 我们可以在then方法中再 new 一个Promise,然后把它返回
- 使用try....catch
- 链式调用,返回三种,上面只尝试了基本的,返回promise还有thenable没有处理,返回promise这时就是调用新创建的promise 的resolve
点击查看代码
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 JSPromise {
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 JSPromise((resolve, reject) => {
// 1.如果在then调用的时候, 状态已经确定下来
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch (err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch (err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.将成功回调和失败的回调放到数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch (err) {
// reject(err)
// }
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch (err) {
// reject(err)
// }
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
}
const promise = new JSPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111) // resolved/fulfilled
//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)
})
catch 方法
我们可以在then中传入第二个回调,也可以通过catch, 下面我们来实现catch方法,使用这个catch是new出来的promise返回值的这个promise点击查看代码
class JSPromise {
catch(onRejected) {
return this.then(undefined, onRejected);
}
}
finally
点击查看代码
class JSPromise {
finally(onFinally) {
return this.then(
() => {
onFinally();
},
() => {
onFinally();
}
);
}
}
增加了上面的代码,我们测试一下,会有如下问题:
resolve/reject
点击查看代码
static resolve(value) {
return new JSPromise((resolve) => resolve(value))
}
static reject(reason) {
return new JSPromise((resolve, reject) => reject(reason))
}
all/allSettled
点击查看代码
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new JSPromise((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 JSPromise((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)
}
})
})
})
}
race/any
点击查看代码
static race(promises) {
return new JSPromise((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 JSPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
♣ 手写简单Promise 完整代码
点击查看代码
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 JSPromise {
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 JSPromise((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 JSPromise((resolve) => resolve(value))
}
static reject(reason) {
return new JSPromise((resolve, reject) => reject(reason))
}
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new JSPromise((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 JSPromise((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 JSPromise((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 JSPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}