介绍
在本文中,将会通过测试先行的方式来教你创建一个 Promise
正文
为了更好的梳理思路,可以采用测试先行的方式来实现 Promise,这里我使用jest作为功能测试框架
带有注释的是每一步新增的代码
3.1 constructor
1. 应该立刻执行构造函数传入的代码
Test Case
test('应该立刻执行构造函数传入的代码', () => {
let timer = 0
new _Promise(() => {
timer++
})
expect(timer).toBe(1)
})
Code
class _Promise {
constructor(executor) {
executor()
}
}
2. promise 有 3 种状态
Test Case
test('promise 有 3 种状态', () => {
const p1 = new _Promise()
expect(p1.status).toBe('pending')
const p2 = new _Promise(resolve => resolve())
expect(p2.status).toBe('fulfilled')
const p3 = new _Promise((resolve, reject) => reject())
expect(p3.status).toBe('rejected')
})
Code
// 新增 3 种状态
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
class _Promise {
constructor(executor = () => {}) {
// 立即执行构造函数,并且状态变为 pending
this.status = STATUS_PENDING
const resolve = () => {
// 执行 resolve 后,状态变为 fulfilled
this.status = STATUS_FULFILLED
}
const reject = () => {
// 执行 reject 后,状态变为 rejected
this.status = STATUS_REJECTED
}
// 传入的回调会有两个参数 resolve/reject
executor(resolve, reject)
}
}
3. 执行 resolve、reject 后状态固化
Test Case
test('执行 resolve、reject 后状态固化 ', () => {
const p1 = new _Promise((resolve, reject) => {
resolve()
reject()
})
expect(p1.status).toBe('fulfilled')
const p2 = new _Promise((resolve, reject) => {
reject()
resolve()
})
expect(p2.status).toBe('rejected')
})
Code
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
export class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING
const resolve = () => {
// 如果状态时 PENDING 时才会执行代码
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
}
}
const reject = () => {
// 如果状态时 PENDING 时才会执行代码
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
}
}
executor(resolve, reject)
}
}
3.2 then
1. then 方法可以接收两个参数,可以处理 resolve 和 reject
Test Case
test('then 方法可以接收两个参数,可以处理 resolve 和 reject', () => {
new _Promise(resolve => {
resolve('success')
}).then(res => {
expect(res).toBe('success')
})
new _Promise((resolve, reject) => {
reject('error')
}).then(undefined, err => {
expect(err).toBe('error')
})
})
Code
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
export class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING
this.value = undefined
this.reason = undefined
// resolve 需要接收一个参数
const resolve = value => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
// 将接收的参数保存在 Promise 种
this.value = value
}
}
// reject 也可以接收一个参数
const reject = reason => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
}
}
executor(resolve, reject)
}
// 实例方法 then
then(onFulfilled, onRejected) {
if (onFulfilled) onFulfilled(this.value)
if (onRejected) onRejected(this.reason)
}
}
2. executor 可以是一个异步函数
Test Case
test('executor 可以是一个异步函数', () => {
new _Promise(resolve => {
setTimeout(() => {
resolve('success')
}, 300)
}).then(res => {
expect(res).toBe('success')
})
new _Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, 300)
}).then(undefined, err => {
expect(err).toBe('error')
})
})
Code
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING
this.value = undefined
this.reason = undefined
// 因为可以执行多次 then,因此需要将所有的任务放在一个队列中
this.resolveQueue = []
this.rejectQueue = []
const resolve = value => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
// 在 resolve 时执行 resolveQueue 所有的任务
if (this.resolveQueue.length)
this.resolveQueue.forEach(item => item(this.value))
}
}
const reject = reason => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
// 在 reject 时执行 rejectQueue 所有的任务
if (this.rejectQueue.length)
this.rejectQueue.forEach(item => item(this.reason))
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// 由于 executor 可能是一个异步函数,所以就不能直接来执行
// 传入的参数了,需要做一下状态判断
// 如果执行 then 时 Promise 实例的状态已经变化,那么就可以直接执行传入的参数
if (this.status === STATUS_FULFILLED && onFulfilled) {
onFulfilled(this.value)
}
if (this.status === STATUS_REJECTED && onRejected) {
onRejected(this.reason)
}
// 如果在执行 then 的时候当前的状态还是 PENDING
// 那么就加入队列中,等待执行 resolve、reject 的时候统一执行所有的队列
if (this.status === STATUS_PENDING) {
// 将任务放到相应队列中
if (onFulfilled) this.resolveQueue.push(onFulfilled)
if (onRejected) this.resolveQueue.push(onRejected)
}
}
}
3. 如果构造函数抛出了一个错误,then 的第二个参数也可以捕获到
Test Case
test('如果构造函数抛出了一个错误,then 的第二个参数也可以捕获到', () => {
new _Promise(() => {
throw new Error('error!')
}).then(undefined, err => {
expect(err).toEqual(new Error('error!'))
})
})
Code
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING
this.value = undefined
this.reason = undefined
this.resolveQueue = []
this.rejectQueue = []
const resolve = value => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
if (this.resolveQueue.length)
this.resolveQueue.forEach(item => item(this.value))
}
}
const reject = reason => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
if (this.rejectQueue.length)
this.rejectQueue.forEach(item => item(this.reason))
}
}
// try...catch 捕获一下错误
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
// then code 无变化
}
}
4. 链式调用
Test Case
test('链式调用', () => {
new _Promise(resolve => {
resolve('success1')
})
.then(res => {
expect(res).toBe('success1')
return res + ' success2'
})
.then(res => {
expect(res).toBe('success1 success2')
})
new _Promise((resolve, reject) => {
reject('error1')
})
.then(undefined, err => {
expect(err).toBe('error1')
return err + ' error2'
})
.then(res => {
expect(res).toBe('error1 error2')
})
})
Code
_Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// 要想做到链式调用,就需要返回新的 Promise
return new Promise((resolve, reject) => {
if (this.status === STATUS_FULFILLED && onFulfilled) {
// 将 onFulfilled 返回的值作为下一个 Promise resolve() 的值
const value = onFulfilled(this.value)
resolve(value)
}
if (this.status === STATUS_REJECTED && onRejected) {
// 这个也是同理
const reason = onRejected(this.reason)
resolve(reason)
}
if (this.status === STATUS_PENDING) {
// 这里需要做一下处理了,因为这里队列是在构造函数中处理的
// 所以需要转化一下
if (onFulfilled)
this.resolveQueue.push(param => {
const value = onFulfilled(param)
resolve(value)
})
if (onRejected)
this.resolveQueue.push(param => {
const reason = onRejected(param)
resolve(reason)
})
}
})
}
}
5. 在链式调用的过程中出现任何错误,将由下面的 then 第二个参数处理
Test Case
test('在链式调用的过程中出现任何错误,将由下面的 then 第二个参数处理', () => {
new _Promise((resolve, reject) => {
resolve('success')
})
.then(res => {
expect(res).toBe('success')
throw new Error('error!')
})
.then(undefined, err => {
expect(err).toEqual(new Error('error!'))
})
})
Code
// 因为需要对 4 个地方同时进行 try...catch
// 重复代码,就可以抽离为工具函数了
function executeFnWithCatchError(fn, param, resolve, reject) {
try {
const result = fn(param)
resolve(result)
} catch (error) {
reject(error)
}
}
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
if (this.status === STATUS_FULFILLED && onFulfilled) {
// 在这里进行捕获
executeFnWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === STATUS_REJECTED && onRejected) {
// 在这里进行捕获
executeFnWithCatchError(onRejected, this.reason, resolve, reject)
}
if (this.status === STATUS_PENDING) {
if (onFulfilled)
this.resolveQueue.push(param => {
// 在这里进行捕获
executeFnWithCatchError(onFulfilled, param, resolve, reject)
})
if (onRejected)
this.resolveQueue.push(param => {
// 在这里进行捕获
executeFnWithCatchError(onRejected, param, resolve, reject)
})
}
})
}
}
3.3 catch
1. catch 应该捕获上一个 Promise 实例的 reject
Test Case
test('catch 应该捕获上一个 Promise 实例的 reject ', () => {
new _Promise((resolve, reject) => {
reject('error')
}).catch(err => {
expect(err).toBe('error')
})
new _Promise((resolve, reject) => {
resolve('success')
})
.then(() => {
throw new Error('error')
})
.catch(err => {
expect(err).toEqual(new Error('error'))
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 方法无变化
}
// 新增实例方法 catch
catch(onRejected) {
// 这里其实可以直接复用 then 的逻辑
// 将传入的数据作为 then 的第二个参数
this.then(undefined, onRejected)
}
}
2. catch 可以捕获最开始的 reject
Test Case
test('catch 可以捕获最开始的 reject ', () => {
new Promise((resolve, reject) => {
reject('error')
})
.then(res => {
return res
})
.then(res => {
return res
})
.catch(err => {
console.log('error', err)
expect(err).toBe('error')
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// 针对这一情况,可以给两个参数写一个默认值
onFulfilled = onFulfilled
? onFulfilled
: value => {
return value
}
onRejected = onRejected
? onRejected
: reason => {
throw new Error(reason)
}
return new Promise((resolve, reject) => {
// 判断就可以删除掉了,因为传入的两个参数是必定有值的
if (this.status === STATUS_FULFILLED) {
executeFnWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === STATUS_REJECTED) {
executeFnWithCatchError(onRejected, this.reason, resolve, reject)
}
if (this.status === STATUS_PENDING) {
this.resolveQueue.push(param => {
executeFnWithCatchError(onFulfilled, param, resolve, reject)
})
this.resolveQueue.push(param => {
executeFnWithCatchError(onRejected, param, resolve, reject)
})
}
})
}
catch(onRejected) {
// catch 无变化
}
}
3.4 finally
Test Case
test('无论 Promise 状态是满足还是拒绝,finally 都应该执行', () => {
let finallyTimer = 0
new _Promise(resolve => {
resolve('hello world')
}).finally(() => {
finallyTimer++
})
expect(finallyTimer).toBe(1)
new _Promise((resolve, reject) => {
reject('hello world')
}).finally(() => {
finallyTimer++
})
expect(finallyTimer).toBe(2)
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
// 新增实例方法 finally
finally(onFinally) {
// 复用 then 逻辑,传入 onFinally
this.then(
() => {
onFinally()
},
() => {
onFinally()
}
)
}
}
3.5 resolve
resolve 是 Promise 的类方法,可以将传入的值作为resolve()的参数,即:
Promise.resolve("success")
// 等于下面
new Promise(resolve => {
resolve('success')
})
Test Case
test('resolve 可以将一个传入的值作为 resolve() 的参数', () => {
const data = { name: 'alex' }
Promise.resolve(data).then(res => {
expect(res).toEqual(data)
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
finally(onFinally) {
// finally 无变化
}
// 新增类方法
static resolve(data) {
return new _Promise(resolve => resolve(data))
}
}
3.6 reject
和 resolve 的逻辑是一样的
Test Case
test('reject 可以将传入的值作为 reject() 的参数', () => {
_Promise.reject('error').catch(err => {
expect(err).toBe('error')
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
finally(onFinally) {
// finally 无变化
}
static resolve(data) {
// resolve 无变化
}
// 新增类方法 reject
static reject(data) {
return new _Promise((resolve, reject) => reject(data))
}
}
3.7 all
Test Case
test('all 方法接收队列,所有 fulfilled 即 fulfilled,一个 rejected 则 rejected', () => {
function genPromise(index) {
return new _Promise(resolve => {
resolve('success' + index)
})
}
const promiseQueue = [
genPromise(0),
genPromise(1),
genPromise(2),
genPromise(3),
]
_Promise.all(promiseQueue).then(res => {
expect(res).toEqual(['success0', 'success1', 'success2', 'success3'])
})
const promiseQueue2 = [
...promiseQueue,
new _Promise((resolve, reject) => reject('error')),
]
_Promise.all(promiseQueue2).catch(err => {
expect(err).toBe('error')
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
finally(onFinally) {
// finally 无变化
}
static resolve(data) {
// resolve 无变化
}
static reject(data) {
// reject 无变化
}
// 新增类方法
static all(promiseQueue) {
return new _Promise((resolve, reject) => {
const result = []
// 对队列进行遍历
promiseQueue.forEach(promise => {
promise
.then(res => {
result.push(res)
})
.catch(err => {
// 任何一个 reject 那么就直接 reject
reject(err)
})
})
// 所有的 resolve,才 resolve
resolve(result)
})
}
}
3.8 allSettled
Test Case
test('allSettled 无论 promise 队列是 fulfilled 还是 rejected,都会保存起来,并且只会是 fulfilled', () => {
function genPromise(index) {
return new Promise(resolve => {
resolve('success' + index)
})
}
const promiseQueue = [genPromise(0), genPromise(1), genPromise(2)]
Promise.allSettled(promiseQueue).then(res => {
expect(res).toEqual([
{ status: 'fulfilled', value: 'success0' },
{ status: 'fulfilled', value: 'success1' },
{ status: 'fulfilled', value: 'success2' },
])
})
const promiseQueue2 = [
...promiseQueue,
new Promise((resolve, reject) => reject('error')),
]
Promise.allSettled(promiseQueue2).catch(err => {
expect(err).toEqual([
{ status: 'fulfilled', value: 'success0' },
{ status: 'fulfilled', value: 'success1' },
{ status: 'fulfilled', value: 'success2' },
{ status: 'rejected', reason: 'error' },
])
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
finally(onFinally) {
// finally 无变化
}
static resolve(data) {
// resolve 无变化
}
static reject(data) {
// reject 无变化
}
static all(promiseQueue) {
// all 无变化
}
// 新增类方法:allSettled
static allSettled(promiseQueue) {
return new _Promise(resolve => {
const result = []
// 遍历队列,满足 or 拒绝都加入返回队列中
// 只是状态和 value/reason 不同
promiseQueue.forEach(promise => {
promise
.then(res => {
result.push({
status: STATUS_FULFILLED,
value: res,
})
})
.catch(err => {
result.push({
status: STATUS_REJECTED,
reason: err,
})
})
})
resolve(result)
})
}
}
3.9 race
Test Case
test('队列中哪个最先完成,那么 race 返回的实例就是什么状态', () => {
function genPromise(delay, message, type = 'fulfilled') {
if (type === 'fulfilled') {
return new _Promise(resolve => {
setTimeout(() => {
resolve(message)
}, delay)
})
}
return new _Promise((resolve, reject) => {
setTimeout(() => {
reject(message)
}, delay)
}, delay)
}
const promiseQueue = [
genPromise(200, 'success1'),
genPromise(100, 'success2'),
genPromise(300, 'success3'),
]
_Promise.race(promiseQueue).then(res => {
expect(res).toBe('success2')
})
const promiseQueue2 = [...promiseQueue, genPromise(50, 'error', 'rejected')]
_Promise.race(promiseQueue2).catch(err => {
expect(err).toBe('error')
})
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
finally(onFinally) {
// finally 无变化
}
static resolve(data) {
// resolve 无变化
}
static reject(data) {
// reject 无变化
}
static all(promiseQueue) {
// all 无变化
}
// 新增类方法:allSettled
static allSettled(promiseQueue) {
// allSettled 无变化
}
// 新增类方法 race
static race(promiseQueue) {
return new _Promise((resolve, reject) => {
// 遍历队列
promiseQueue.forEach(promise => {
// 有一个改变了状态,那么整体就改变了状态
promise
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
})
}
}
3.10 any
Test Case
test('any 方法接收一个 Promise 队列,只要一个是 fulfilled 状态,就改变返回的实例状态,如果所有都是 rejected,也要等待所有执行完毕后,抛出一个错误', () => {
function genPromise(delay, message, type = 'fulfilled') {
if (type === 'fulfilled') {
return new _Promise(resolve => {
setTimeout(() => {
resolve(message)
}, delay)
})
}
return new _Promise((resolve, reject) => {
setTimeout(() => {
reject(message)
}, delay)
}, delay)
}
const promiseQueue = [
genPromise(100, 'error1', 'rejected'),
genPromise(200, 'success1'),
genPromise(300, 'error2', 'rejected'),
]
_Promise
.any(promiseQueue)
.then(res => {
expect(res).toBe('success1')
})
.catch(() => {})
const promiseQueue2 = [
genPromise(100, 'error11', 'rejected'),
genPromise(200, 'error22', 'rejected'),
genPromise(300, 'error33', 'rejected'),
]
try {
_Promise.any(promiseQueue2).catch(err => {})
} catch (error) {
expect(error).toEqual(new AggregateError('All promises were rejected'))
}
})
Code
class _Promise {
constructor(executor = () => {}) {
// 构造函数无变化
}
then(onFulfilled, onRejected) {
// then 无变化
}
catch(onRejected) {
// catch 无变化
}
finally(onFinally) {
// finally 无变化
}
static resolve(data) {
// resolve 无变化
}
static reject(data) {
// reject 无变化
}
static all(promiseQueue) {
// all 无变化
}
static allSettled(promiseQueue) {
// allSettled 无变化
}
static race(promiseQueue) {
// race 无变化
}
// 新增类方法 any
static any(promiseQueue) {
return new _Promise((resolve, reject) => {
const reasons = []
promiseQueue.forEach(promise => {
promise
.then(res => {
// 只要有一个满足,那么就满足
resolve(res)
})
.catch(err => {
reasons.push(err)
})
})
// 如果全部拒绝,那么就抛出一个错误
if (reasons.length === promiseQueue.length) {
throw new AggregateError()
}
})
}
}
3.11 全部
最后,关于 Promise 的功能和 API 我们已经初步实现完毕了,当然,一些边界判断是没有加的。
让我们看一下全部代码吧:
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
function executeFnWithCatchError(fn, param, resolve, reject) {
try {
const result = fn(param)
resolve(result)
} catch (error) {
reject(error)
}
}
export class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING
this.value = undefined
this.reason = undefined
this.resolveQueue = []
this.rejectQueue = []
const resolve = value => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
if (this.resolveQueue.length)
this.resolveQueue.forEach(item => item(this.value))
}
}
const reject = reason => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
if (this.rejectQueue.length)
this.rejectQueue.forEach(item => item(this.reason))
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = onFulfilled
? onFulfilled
: value => {
return value
}
onRejected = onRejected
? onRejected
: reason => {
throw new Error(reason)
}
return new Promise((resolve, reject) => {
if (this.status === STATUS_FULFILLED) {
executeFnWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === STATUS_REJECTED) {
executeFnWithCatchError(onRejected, this.reason, resolve, reject)
}
if (this.status === STATUS_PENDING) {
this.resolveQueue.push(param => {
executeFnWithCatchError(onFulfilled, param, resolve, reject)
})
this.resolveQueue.push(param => {
executeFnWithCatchError(onRejected, param, resolve, reject)
})
}
})
}
catch(onRejected) {
this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(
() => {
onFinally()
},
() => {
onFinally()
}
)
}
static resolve(data) {
return new _Promise(resolve => resolve(data))
}
static reject(data) {
return new _Promise((resolve, reject) => reject(data))
}
static all(promiseQueue) {
return new _Promise((resolve, reject) => {
const result = []
promiseQueue.forEach(promise => {
promise
.then(res => {
result.push(res)
})
.catch(err => {
reject(err)
})
})
resolve(result)
})
}
static allSettled(promiseQueue) {
return new _Promise(resolve => {
const result = []
promiseQueue.forEach(promise => {
promise
.then(res => {
result.push({
status: STATUS_FULFILLED,
value: res,
})
})
.catch(err => {
result.push({
status: STATUS_REJECTED,
reason: err,
})
})
})
resolve(result)
})
}
static race(promiseQueue) {
return new _Promise((resolve, reject) => {
promiseQueue.forEach(promise => {
promise
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
})
}
static any(promiseQueue) {
return new _Promise((resolve, reject) => {
const reasons = []
promiseQueue.forEach(promise => {
promise
.then(res => {
resolve(res)
})
.catch(err => {
reasons.push(err)
})
})
if (reasons.length === promiseQueue.length) {
throw new AggregateError()
}
})
}
}
总结
本文详细讲解了 Promise 以及手写了 Promise 的逻辑,希望对你有所帮助
附完整源码:
(mini-promise 源码)[github.com/alexzhang10…]