实现一个promise
摘要
之前在网上看了很多关于promise的实现,但是多多少少都有问题,甚至有的实现还很容易误导读者对promise的理解,所以这里结合观察原生promise的行为来实现一个promise,尽可能的接近原生的promise,同时这也可能是全网最正确
的实现方式 为了同时兼容nodejs和chrome,这里使用的是queueMicrotask,当然如果要想获得最好的浏览器端兼容性建议使用MutationObserver
1. 从Promise的基本用法入手
const promise = new Promise((resolve, reject) => {
// do something
resolve('success')
}).then((res) => {
// do something
console.log(res)
}).catch((err) => {})
从上面的代码可以看出,Promise是一个类,构造函数接受一个函数作为参数,这个函数有两个参数,分别是resolve和reject,这两个参数也是函数,分别用来改变Promise的状态。
通俗的理解就是:传入一个带有resolve, reject两个参数的函数,我们可以在这个函数中做些事情,resolve, reject就是两个传话筒,当我们想告诉外界我们做完了,就可以调用resolve,当我们想告诉外界我们失败了,就可以调用reject。而then和catch方法就是用来向Promise注册解决了和拒绝之后要做什么。
根据上面的通俗理解,因此很容易想到Promise必须要有:
三种状态:
待解决
,已经解决
,拒绝解决
,分别对应pending
,fulfilled
,rejected
。
一个状态变量:
_status
,用来表示当前Promise是三个状态的哪一个。
两个事件列表变量:
_fulfilledQueues
、_rejectedQueues
,分别用来储存解决了和拒绝之后要做的事情
一个值变量:
_value
,用来储存resolve或者reject传入的值。
2. 开始实现Promise的构造函数
const isFunction = (variable) => typeof variable === 'function'
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor(handle){
if (!isFunction(handle)) throw new Error('MyPromise must accept a function as a parameter')
this._status = PENDING
this._value = undefined
this._fulfilledQueues = []
this._rejectedQueues = []
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
}
3. 实现resolve和reject两个传话筒
实现之前 还是先观察原生的Promise的行为
const promise1 = new Promise((resolve, reject) => {
resolve(1)
})
console.log(123)
console.log(promise1)
/* 输出 :
123
Promise {<fulfilled>: 1}
*/
const promise2 = new Promise((resolve, reject) => {
resolve(promise1)
})
const promise3 = new Promise((resolve, reject) => {
resolve(promise2)
})
console.log(456)
console.log(promise3)
queueMicrotask(() => {
console.log("第一层:",promise3)
queueMicrotask(() => {
console.log("第三层:",promise3)
queueMicrotask(() => {
console.log("第三层:",promise3)
})
})
})
/* 输出
123
Promise {<fulfilled>: 1}
456
Promise {<pending>}
第一层: Promise {<pending>}
第二层: Promise {<pending>}
第三层: Promise {<fulfilled>: 1}
*/
结论
:resolve(非promise)执行会立即改变状态。resolve(Promise) 不会立刻改变状态,而是会等到传入的Promise状态改变之后再改变状态。也就是要等调用promise的then方法callback的时候才会执行promise2的resolve,过程是promise1.then(v=>promise2.resolve(v)) -> promise2.then(v=>promise3.resolve(v)),这三步都是微任务异步的,这也解释了为什么第三层promise3才被解决
const promise = new Promise((resolve, reject) => {
resolve(1)
}).then((res) => {console.log("then called",res)})
console.log(123)
console.log(promise)
queueMicrotask(() => {console.log(promise)})
/* 输出 :
123
Promise {<pending>}
then called 1
Promise {<fulfilled>: undefined}
*/
结论
: resolve(1)执行会立即改变状态,但是then 会返回一个pending状态的的Promise,该promise会在微任务队列解决。
reject的行为比较简单这里就不累赘了,自己可以验证一下。开始实现resolve和reject
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== PENDING) return
if (!(val instanceof this.constructor)) {
this._status = FULFILLED
this._value = val
}
const run = () => {
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
let cb
while ((cb = this._fulfilledQueues.shift())) {
cb(value)
}
}
/* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
*/
if (val instanceof this.constructor) {
val.then(
(value) => {
this._resolve(value)
},
(err) => {
this._reject(err)
}
)
} else {
runFulfilled(val)
}
}
if (queueMicrotask) {
queueMicrotask(run)
} else {
setTimeout(run, 0)
}
}
_reject (err) {
console.log(err, this);
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
// 依次执行失败队列中的函数,并清空队列
const run = () => {
let cb
while ((cb = this._rejectedQueues.shift())) {
cb(err)
}
}
if (queueMicrotask) {
queueMicrotask(run)
} else {
setTimeout(run, 0)
}
}
4.实现then方法
then的特点是返回一个新的promise,并且可以链式调用。then如果返回非promise的值,则新的promise状态为fulfilled,值为返回值。如果返回的是promise,则需要等待promise状态改变后,新的promise状态才会改变,且状态取决于参数Promsie对象的状态。
then (onFulfilled, onRejected) {
const { _value, _status } = this
return new this.constructor((onFulfilledNext, onRejectedNext) => {
let fulfilled = (value) => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value)
if (res instanceof this.constructor) {
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
let rejected = (error) => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error)
if (res instanceof this.constructor) {
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
switch (_status) {
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
case FULFILLED:
queueMicrotask(() => fulfilled(_value))
break
case REJECTED:
queueMicrotask(() => rejected(_value))
break
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
最后几个静态方法和factory方法就不写了,比较简单。 需要说明的一点是:
// 添加静态reject方法
static reject (value) {
return new this((resolve, reject) => reject(value))
}
这里使用this
而不是MyPromise
,是因为MyPromise
是类名,而this
指向的是当前实例的构造函数,这样写可以使用call改变this。可以在任何实现与 Promise() 构造函数相同签名的构造函数上调用,如果写死MyPromise就不能实现调用了。例如下面是MDN上面的一个例子,我们可以在一个构造函数上调用它,并传入 console.log 作为 reject 参数:
class NotPromise {
constructor(executor) {
// “resolve”和“reject”函数的行为与原生 Promise 完全不同,但 `Promise.reject()` 方法以相同的方式调用它们。
executor(
(value) => console.log("已解决", value),
(reason) => console.log("已拒绝", reason),
);
}
}
Promise.reject.call(NotPromise, "foo"); // 输出 "已拒绝 foo"
// 我们的MyPromise也可以这样调用
MyPromise.reject.call(NotPromise, "foo"); // 输出 "已拒绝 foo"
完整代码
const isFunction = (variable) => typeof variable === 'function'
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor(handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
this._status = PENDING
this._value = undefined
this._fulfilledQueues = []
this._rejectedQueues = []
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== PENDING) return
if (!(val instanceof this.constructor)) {
this._status = FULFILLED
this._value = val
}
const run = () => {
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
let cb
while ((cb = this._fulfilledQueues.shift())) {
cb(value)
}
}
/* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
*/
if (val instanceof this.constructor) {
val.then(
(value) => {
this._resolve(value)
},
(err) => {
this._reject(err)
}
)
} else {
runFulfilled(val)
}
}
if (queueMicrotask) {
queueMicrotask(run)
} else {
setTimeout(run, 0)
}
}
_reject (err) {
console.log(err, this);
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
// 依次执行失败队列中的函数,并清空队列
const run = () => {
let cb
while ((cb = this._rejectedQueues.shift())) {
cb(err)
}
}
if (queueMicrotask) {
queueMicrotask(run)
} else {
setTimeout(run, 0)
}
}
then (onFulfilled, onRejected) {
const { _value, _status } = this
return new this.constructor((onFulfilledNext, onRejectedNext) => {
let fulfilled = (value) => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value)
if (res instanceof this.constructor) {
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
let rejected = (error) => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error)
if (res instanceof this.constructor) {
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
switch (_status) {
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
case FULFILLED:
queueMicrotask(() => fulfilled(_value))
break
case REJECTED:
queueMicrotask(() => rejected(_value))
break
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof this) return value
return new this((resolve) => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new this((resolve, reject) => reject(value))
}
// 添加静态all方法
static all (list) {
return new this((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(
(res) => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
},
(err) => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
}
)
}
})
}
// 添加静态race方法
static race (list) {
return new this((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
}
})
}
finally (cb) {
return this.then(
(value) => this.constructor.resolve(cb()).then(() => value),
(reason) =>
this.constructor.resolve(cb()).then(() => {
throw reason
})
)
}
}