前言
Promise 诞生于 ES2015(ES6),是 JS 主流的异步操作解决方案,也是前端面试过程中的必考题。作为前端开发,必须要能够熟练运用 Promise 并理解原理。
本文将一步一步分析 Promise 的特征与用法,并最终实现一个包含以下 API 的 Promise:
-
Promise.prototype.then() -
Promise.prototype.catch() -
Promise.prototype.finally() -
Promise.all() -
Promise.resolve() -
Promise.reject()
具体实现步骤会在代码中通过注释说明。
为了避免代码重复,下面的每一步的实现过程都会忽略其他步骤的代码,只包含当前步骤的必要代码。完整代码将放在最后。
1. Promise 基本用法
1.1. 创建 Promise
const asyncFunc = new Promise((resolve, rejcet) => {
Math.random() > 0.5 ? resolve('some data') : rejcet('some reason')
})
1.2. 处理 Promise
asyncFunc()
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
.finally(() => {
console.log('finally')
})
2. 实现 Promise
2.1. 声明状态
Promise 有三种状态:pending、fulfilled 和 rejected,状态只能改变一次,之后任何时候都可以得到这个结果。
graph TD
PENDING --> FULFILLED
PENDING --> REJECTED
这里先将它们枚举出来,后续会大量用到:
// pending(待定,初始状态)
const PENDING = 'pending'
// fulfilled(已兑现,意味着操作成功)
const FULFILLED = 'fulfilled'
// rejected(已拒绝,意味着操作失败)
const REJECTED = 'rejected'
2.2. 实现 reject() 和 resolve()
根据 Promise 的用法我们知道,Promise 是一个构造函数,它接受一个普通函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们也是函数,用来改变 Promise 的状态。
class MyPromise {
constructor(executor) {
// 利用 try/catch 捕获错误
try {
// 执行传入的函数
executor(this.resolve, this.reject)
} catch (error) {
// 执行错误直接 reject
this.reject(error)
}
}
// 设置初始状态为 PENDING
status = PENDING
// 存储 resolve 后返回的数据
data = undefined
// 存储 reject 后返回的原因
reason = undefined
// 成功
resolve = data => {
// 状态改变后就不能再变了
if (this.status !== PENDING) return
// 更改状态
this.status = FULFILLED
// 保存数据
this.data = data
}
// 失败
reject = reason => {
// 状态改变后就不能再变了
if (this.status !== PENDING) return
// 更改状态
this.status = REJECTED
// 保存原因
this.reason = reason
}
}
2.3. 实现 .then()
.then() 方法是 Promise 的核心之一,异步操作的成功或失败,都可以通过传递给 .then() 方法的回调函数进行处理。并且它会继续返回一个 Promise 对象,这样可以通过多次调用 .then() 添加多个回调函数,它们会按照插入的顺序执行,形成链式调用(chaining)。
class MyPromise {
// 存储 resolve 的回调函数
fulfilledCallback = []
// 存储 reject 的回调函数
rejectedCallback = []
// 成功
resolve = data => {
// 依次调用成功回调
while (this.fulfilledCallback.length) {
this.fulfilledCallback.shift()(this.data)
}
}
// 失败
reject = reason => {
// 依次调用失败回调
while (this.rejectedCallback.length) {
this.rejectedCallback.shift()(this.reason)
}
}
// .then()
then(onResolved = data => data /*设置默认的成功回调 */, onRejected) {
// 创建一个新的 Promise 并 return,以供链式调用
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 异步执行,用来获取新的 promise
setTimeout(() => {
try {
const value = onResolved(this.data)
// 判断返回值是普通值还是 Promise
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
return
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const value = onRejected(this.reason)
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
return
}
// 将回调函数存入数组
this.fulfilledCallback.push(() => {
setTimeout(() => {
try {
let value = onResolved(this.data)
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
// 将回调函数存入数组
this.rejectedCallback.push(() => {
setTimeout(() => {
try {
let value = onRejected(this.reason)
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
})
return promise
}
}
2.4. 实现 .catch()
实现 .then() 之后,.catch() 就会简单很多。因为 .catch() 只是没有给 fulfilled 状态预留参数位置的 .then() 而已,所以这里直接返回一个没有成功回调函数的 .then() 即可。
class MyPromise {
// .catch()
catch(onRejected) {
// .catch() 只是没有给 fulfilled 状态预留参数位置的 .then()
return this.then(undefined, onRejected)
}
}
2.5. 实现 .finally()
有些业务需要在不管成功还是失败时都进行,为了避免了代码重复,所以有了 .finally() 方法,无论是 Promise 最终是 fulfilled 还是 rejected 状态,.finally() 都会执行,但区别在于 .finally() 的回调函数中没有参数。
class MyPromise {
// .finally()
finally(callback) {
// 因为 .then 也会在两种状态下都执行,所以这里复用即可,并且在两种状态下都执行同一个无参数的回调函数
return this.then(
data => {
return MyPromise.resolve(callback()).then(() => data)
},
err => {
return MyPromise.resolve(callback()).then(() => {
throw err
})
}
)
}
}
2.6. 实现 Promise.all()
和 .then()、.catch()、.finally() 不同,Promise.all() 是一个静态方法,用于集合多个 Promise 的返回结果。
用法:
const asyncFuncFirst = new Promise(() => { /* do something */ })
const asyncFuncSecond = new Promise(() => { /* do something */ })
const asyncFuncThird = new Promise(() => { /* do something */ })
Promise.all([asyncFuncFirst, asyncFuncSecond, asyncFuncThird])
.then(([resFirst, resSecond, resThird]) => {
/* do something */
})
.catch(err => {
/* do something */
})
.finally(() => {
/* do something */
})
实现:
class MyPromise {
static all(funcs) {
// 记录执行次数,用户判断是否已经全部执行
let times = 0
// 保存执行结果
let result = []
// 会返回一个 Promise
return new MyPromise((resolve, reject) => {
// 记录结果
function addData(key, value) {
times++
result[key] = value
times === funcs.length && resolve(result)
}
// 依次执行,并保存执行结果
funcs.forEach((element, index) => {
// 判断元素是否为 Promise 对象
element instanceof MyPromise
? element.then(
data => addData(index, data),
err => reject(err) // 任何一个 Promise 对象的 reject 被执行都会立即 reject()
)
: addData(index, element) // 非 promise 的元素将被直接放在返回数组中
})
})
}
}
2.7. 实现 Promise.resolve()
Promise.resolve() 方法返回一个以给定值解析后的 Promise 对象。
class MyPromise {
// Promise.resolve()
static resolve(value) {
// 返回一个以给定值解析后的 Promise 对象
return value instanceof MyPromise
? value
: new MyPromise(resolve => resolve(value))
}
}
2.8. 实现 Promise.reject()
Promise.reject() 方法返回一个带有拒绝原因的 Promise 对象。
class MyPromise {
//...
// Promise.reject()
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error)
})
}
// ...
}
3. 完整代码
// 声明 Promise 的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 以构造函数的形式实现
class MyPromise {
constructor(executor) {
// 利用 try/catch 捕获错误
try {
// 执行传入的函数
executor(this.resolve, this.reject)
} catch (error) {
// 执行错误直接 reject
this.reject(error)
}
}
// 设置初始状态为 PENDING
status = PENDING
// 存储 resolve 后返回的数据
data = undefined
// 存储 reject 后返回的原因
reason = undefined
// 存储 resolve 的回调函数列表
fulfilledCallback = []
// 存储 reject 的回调函数列表
rejectedCallback = []
// 成功
resolve = data => {
// 状态改变后就不能再变了
if (this.status !== PENDING) return
// 更改状态
this.status = FULFILLED
// 保存数据
this.data = data
// 依次调用成功回调
while (this.fulfilledCallback.length) {
this.fulfilledCallback.shift()(this.data)
}
}
// 失败
reject = reason => {
// 状态改变后就不能再变了
if (this.status !== PENDING) return
// 更改状态
this.status = REJECTED
// 保存原因
this.reason = reason
// 依次调用失败回调
while (this.rejectedCallback.length) {
this.rejectedCallback.shift()(this.reason)
}
}
// then:处理 resolve 和 reject
then(onResolved = data => data /*设置默认的成功回调 */, onRejected) {
// 创建一个新的 Promise 并 return,以供链式调用
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 转换为 异步执行,用来获取 新的 promise
setTimeout(() => {
try {
const value = onResolved(this.data)
// 判断返回值是普通值还是 Promise
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
return
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const value = onRejected(this.reason)
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
return
}
// 将回调函数存入数组
this.fulfilledCallback.push(() => {
setTimeout(() => {
try {
let value = onResolved(this.data)
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
// 将回调函数存入数组
this.rejectedCallback.push(() => {
setTimeout(() => {
try {
let value = onRejected(this.reason)
resolvePromise(promise, value, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
})
return promise
}
// .catch()
catch(onRejected) {
// 事实上 .catch() 只是没有给 fulfilled 状态预留参数位置的 .then()
return this.then(undefined, onRejected)
}
// .finally()
finally(callback) {
// 因为 .then 也会在两种状态下都执行,所以这里复用即可,并且在两种状态下都执行同一个无参数的回调函数
return this.then(
data => {
return MyPromise.resolve(callback()).then(() => data)
},
err => {
return MyPromise.resolve(callback()).then(() => {
throw err
})
}
)
}
// Promise.all()
static all(iterable) {
// 记录执行次数,用户判断是否已经全部执行
let times = 0
// 保存执行结果
let result = []
// 返回一个 Promise
return new MyPromise((resolve, reject) => {
// 记录结果
function addData(key, value) {
times++
result[key] = value
times === iterable.length && resolve(result)
}
// 依次执行,然后将结果保存到数组中
iterable.forEach((element, index) => {
// 判断元素是否为 Promise 对象
element instanceof MyPromise
? element.then(
data => addData(index, data),
err => reject(err) // 任何一个 Promise 对象的 reject 被执行都会立即 reject()
)
: addData(index, element) // 非 promise 的元素将被直接放在返回数组中
})
})
}
// Promise.resolve()
static resolve(value) {
// 返回一个以给定值解析后的 Promise 对象
return value instanceof MyPromise ? value : new MyPromise(resolve => resolve(value))
}
// Promise.reject()
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error)
})
}
}
// 判断 Promise 的返回值类型
function resolvePromise(promise, value, resolve, reject) {
// 循环调用报错
if (promise === value) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 如果是 Promise 对象
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
resolve(value)
}
}
export default MyPromise