万字详解JavaScript手写一个Promise
本文详细解析JavaScript中Promise的核心原理,从状态机模型出发,逐步实现MyPromise类。涵盖then方法的链式调用、异步回调存储、错误捕获机制,以及all、resolve、finally、catch等静态与实例方法的完整实现,帮助开发者深入理解异步编程底层逻辑。
前言
在当前的前端面试中,手写Promise已成为高频考点。尽管实际业务开发中极少需要从零实现Promise,但掌握其内部机制有助于提升技术上限,增加获得心仪工作的机会。
Promise核心原理实现
我们首先从使用场景出发,分析Promise的行为特征,进而构建一个最基础的Promise实现版本。
Promise的使用分析
Promise本质上是一个类。在实例化该类时,必须传入一个执行器函数(executor),该函数会在构造过程中立即执行。
Promise内部维护着三种状态:
- pending(等待中)
- fulfilled(已成功)
- rejected(已失败)
状态之间的转换仅有两种合法路径:
- 从pending转为fulfilled
- 从pending转为rejected
一旦状态发生切换,便不可逆,无法再次更改。
- 执行器函数接收两个参数:resolve和reject,二者均为回调函数。调用resolve可将状态从pending变为fulfilled,调用reject则将状态从pending变为rejected。传递给这两个回调函数的参数,将分别作为成功或失败的值。
- Promise实例对象提供一个then方法,该方法接收两个可选的回调函数,分别用于处理成功和失败的状态。then方法内部会根据当前状态决定调用哪个回调。该方法应定义在Promise的原型对象上。
- then方法的回调函数接收一个值:若状态为成功,该值为成功结果;若状态为失败,该值为失败原因。
MyPromise的实现
基于上述分析,我们可以编写如下基础代码:
MyPromise.js
// 定义所有状态的常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// Promise实质上就是一个类,首先创建一个Promise的类
class MyPromise {
// 实例化Promise时需要一个回调函数,该回调函数立即执行
constructor(executor) {
// 在调用executor需要传递两个回调函数,分别是resolve和reject
executor(this.resolve, this.reject)
}
// Promise 的状态
status = PENDING
// 记录成功与失败的值
value = undefined
reason = undefined
resolve = (value) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
// 形参value表示,调用resolve时传递的参数
// 如果当前状态不是pending,就直接跳出该逻辑
if (this.status !== PENDING) return
// 将状态修改为成功
this.status = FULFILLED
// 将传入的值进行保存
this.value = value
}
reject = (reason) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
// 形参reason表示,调用reject时传递的失败的原因
// 如果当前状态不是pending,就直接跳出该逻辑
if (this.status !== PENDING) return
// 将状态修改为失败
this.status = REJECTED
// 保存失败的原因
this.reason = reason
}
// then方法的实现
then (onFulfilled, onRejected) {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {
// 将成功的值作为参数返回
onFulfilled(this.value)
} else if (this.status === REJECTED) {
// 将失败的原因作为参数返回
onRejected(this.reason)
}
}
}
// 导出Promise
module.exports = MyPromise 接下来,我们编写测试代码以验证上述实现是否正确。
验证resolve功能:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
})
promise.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
/* 输出
成功
*/验证reject功能:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
reject('失败')
})
promise.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
/* 输出
失败
*/验证状态不可变性:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
reject('失败')
})
promise.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
/* 输出
成功
*/ 在Promise中加入异步操作
如果代码中包含异步操作,而我们的Promise未做相应处理,则无法正常工作。
例如以下代码:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
setTimeout(resolve, 2000, '成功')
})
promise.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})这段代码在2000毫秒后没有任何输出。为解决此问题,我们需要对编写的类进行如下改进:
- 创建两个实例属性,用于存储传入的成功与失败处理逻辑。
- 为
then方法添加状态为pending时的处理逻辑,此时将传入的属性保存至实例上。 - 在状态变为成功或失败时,调用相应的传入回调函数(前提是实例属性存在函数)。
实现代码如下:
// MyPromise.js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = undefined
reason = undefined
// 存储成功与失败的处理逻辑
onFulfilled = undefined
onRejected = undefined
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
// 如果将状态修复为成功,调用成功的回调
this.onFulfilled && this.onFulfilled(this.value)
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
// 如果将状态修复为失败,调用失败的回调
this.onRejected && this.onRejected(this.reason)
}
then (onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else {
// 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
}
module.exports = MyPromise这里的this.onFulfilled && this.onFulfilled(this.value)表示如果该属性存在,则调用该方法。
现在重新执行之前那段代码,2秒后将成功输出成功。
实现then方法的多次调用
Promise实例中存在一个then方法,允许在Promise实例中链式调用。每个then方法还会返回一个新的Promise实例,
如下图所示:

原生Promise的then方法支持多次调用,而我们目前封装的版本仅支持单次调用。根据新需求,重新改写代码:
实现思路如下:
- 定义一个数组,用于存储多个回调函数。
- 如果是同步执行的代码,执行后立即知道结果,可直接调用回调函数。
- 如果是异步代码,需将每次回调函数保存至数组中,待状态变化时依次调用。
实现代码如下:
// MyPromise.js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = undefined
reason = undefined
// 存储成功与失败的处理逻辑
onFulfilled = []
onRejected = []
resolve = (value) => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
// 如果将状态修复为成功,调用成功的回调
while (this.onFulfilled.length) {
// Array.prototype.shift() 用于删除数组第一个元素,并返回
this.onFulfilled.shift()(this.value)
}
}
reject = (reason) => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
// 如果将状态修复为失败,调用失败的回调
while (this.onRejected.length) {
// Array.prototype.shift() 用于删除数组第一个元素,并返回
this.onRejected.shift()(this.reason)
}
}
then (onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else {
// 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
}
}
}
module.exports = MyPromise这里通过数组的shift()方法,该方法删除数组第一个元素并返回,返回的值即为一个回调函数,调用该函数即可实现功能。
实现then的链式调用
要实现then的链式调用,主要解决两个问题:
- 返回的是一个全新的
MyPormise实例。 then的返回值将作为下一次链式调用的参数。
这里分为两种情况:
- 直接返回一个值,可直接作为值使用
- 返回一个新的
MyPormise实例,此时需判断其状态
实现代码如下:
// MyPromise.js
/* 省略的代码同上 */
class MyPromise {
/* 省略的代码同上 */
// then方法的实现
then (onFulfilled, onRejected) {
// then 方法返回一个MyPromise实例
return new MyPromise((resolve, reject) => {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {
// 将成功的值作为参数返回
// 保存执行回调函数的结果
const result = onFulfilled(this.value)
// 如果返回的是一个普通的值,直接调用resolve
// 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
resolvePromise(result, resolve, reject)
} else if (this.status === REJECTED) {
// 将失败的原因作为参数返回
onRejected(this.reason)
} else {
// 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
}
})
}
}
function resolvePromise (result, resolve, reject) {
// 判断传递的result是不是MyPromise的实例对象
if (result instanceof MyPromise) {
// 说明是MyPromise的实例对象
// 调用.then方法,然后在回调函数中获取到具体的值,然后调用具体的回调
// result.then(value => resolve(value), reason => reject(reason))
// 简写
result.then(resolve, reject)
} else {
resolve(result)
}
}
module.exports = MyPromisethen方法链式调用识别Promise对象自返回
在Promise中,如果then方法返回的是自己的promise对象,则会发生promise的嵌套,此时程序会报错,
测试代码如下:
var promise = new Promise((resolve, reject) => {
resolve(100)
})
var p1 = promise.then(value => {
console.log(value)
return p1
})
// 100
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>解决该问题的方法是在then方法返回的MyPromise实例对象与then中回调函数返回的值进行比对,如果相同,则返回一个reject的MyPromise实例对象,并创建一个TypeError类型的Error。
实现代码如下:
// MyPromise.js
/* 省略的代码同上 */
then (onFulfilled, onRejected) {
// then 方法返回一个MyPromise实例
const promise = new MyPromise((resolve, reject) => {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {
// 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
// 如果不变成异步的话是在函数内获取不到promise的
setTimeout(() => {
// 将成功的值作为参数返回
// 保存执行回调函数的结果
const result = onFulfilled(this.value)
// 如果返回的是一个普通的值,直接调用resolve
// 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
resolvePromise(promise, result, resolve, reject)
}, 0)
} else if (this.status === REJECTED) {
// 将失败的原因作为参数返回
onRejected(this.reason)
} else {
// 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
}
})
return promise
}
}
function resolvePromise (promise, result, resolve, reject) {
// 这里修改一下该函数,如果return的Promise实例对象,也就是传入的promise===result的话,说明在promise中return的是当前promise对象。
if (promise === result) {
// 这里调用reject,并抛出一个Error
// return 是必须的,阻止程序向下执行
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断传递的result是不是MyPromise的实例对象
if (result instanceof MyPromise) {
// 说明是MyPromise的实例对象
// 调用.then方法,然后在回调函数中获取到具体的值,然后调用具体的回调
// result.then(value => resolve(value), reason => reject(reason))
// 简写
result.then(resolve, reject)
} else {
resolve(result)
}
}
module.exports = MyPromise这里then方法中的setTimeout的作用并非延迟执行,而是为了调用resolvePromise函数时,保证创建的promise存在。
捕获错误及 then 链式调用其他状态代码补充
到目前为止,我们实现的Promise并未对异常做任何处理。为保证代码健壮性,需对异常进行处理。
捕获执行器错误
现在我们需要对执行器进行异常捕获,如果发生异常,就将状态修改为rejected。
捕获执行器错误相对简单,只需在构造函数中加入try...catch语句即可,
实现代码如下:
constructor(executor) {
try {
// 在调用executor需要传递两个回调函数,分别是resolve和reject
executor(this.resolve, this.reject)
} catch (e) {
// 发生异常调用reject
this.reject(e)
}
}测试代码如下:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
throw new Error('执行器错误')
})
promise.then(value => {
console.log(value);
}, error => {
console.log(error.message);
})
/* 输出
执行器错误
*/捕获then中的报错
现在我们需要对then中的异常进行捕获,并在下一次链式调用中传递到then的第二个函数中。实现方式同样是通过try...catch语句,
示例代码如下:
// then方法的实现
then (onFulfilled, onRejected) {
// then 方法返回一个MyPromise实例
const promise = new MyPromise((resolve, reject) => {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {
// 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
// 如果不变成异步的话是在函数内获取不到promise的
setTimeout(() => {
try {
// 将成功的值作为参数返回
// 保存执行回调函数的结果
const result = onFulfilled(this.value)
// 如果返回的是一个普通的值,直接调用resolve
// 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
// 将失败的原因作为参数返回
onRejected(this.reason)
} else {
// 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
}
})
return promise
}测试代码如下:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
})
// 第一个then方法中的错误要在第二个then方法中捕获到
promise.then(value => {
console.log('resolve', value)
throw new Error('then的执行过程中遇到异常')
}).then(null, reason => {
console.log(reason.message)
})
/* 输出
resolve 成功
then的执行过程中遇到异常
*/错误与异步状态的链式调用
目前仅对成功状态的then进行了链式调用及错误处理,错误与异步状态未处理。其实处理方式类似,
示例代码如下:
// then方法的实现
then (onFulfilled, onRejected) {
// then 方法返回一个MyPromise实例
const promise = new MyPromise((resolve, reject) => {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {
// 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
// 如果不变成异步的话是在函数内获取不到promise的
setTimeout(() => {
try {
// 将成功的值作为参数返回
// 保存执行回调函数的结果
const result = onFulfilled(this.value)
// 如果返回的是一个普通的值,直接调用resolve
// 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
// 失败的处理同成功处理,只是调用的回调函数不同
setTimeout(() => {
try {
const result = onRejected(this.reason)
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
this.onFulfilled.push((value) => {
setTimeout(() => {
try {
const result = onFulfilled(value)
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejected.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(reason)
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise
}测试代码如下:
const MyPromise = require('./myPromise')
let promise = new MyPromise((resolve, reject) => {
setTimeout(resolve, 2000, '成功')
})
// 第一个then方法中的错误要在第二个then方法中捕获到
promise.then(value => {
console.log('resolve', value)
throw new Error('then的执行过程中遇到异常')
}).then(null, reason => {
console.log(reason.message)
})
/* 输出
resolve 成功
then的执行过程中遇到异常
*/将then方法的参数变成可选参数
Promise中的then方法其实包含两个可选参数。如果不传递任何参数,结果将向下传递,直到被捕获为止,
例如下面这段代码:
new Promise((resolve, reject) => {
resolve(100)
})
.then()
.then()
.then()
.then(value => console.log(value))
// 最后一个then输入100 这段代码可以理解为:
new Promise((resolve, reject) => {
resolve(100)
})
.then(value => value)
.then(value => value)
.then(value => value)
.then(value => console.log(value))因此,我们只需在没有传递回调函数时,赋值一个默认的回调函数即可。
实现代码如下:
// then方法的实现
then (onFulfilled, onRejected) {
// 如果传递函数,就是用传递的函数,否则指定一个默认值,用于参数传递
onFulfilled = onFulfilled ? onFulfilled : value => value
// 同理
onRejected = onRejected ? onRejected : reason => { throw reason }
// then 方法返回一个MyPromise实例
const promise = new MyPromise((resolve, reject) => {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {...
} else if (this.status === REJECTED) {...
} else {...
}
})
return promise
}Promise.all方法的实现
关于all()方法的使用,可参考Promise.all()。简单来说,Promise.all()会将多个Promise实例包装为一个Promise实例,且顺序与调用顺序一致,
示例代码如下:
function p1 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 0)
})
}
Promise.all(['a', 'b', p1(), p2(), 'c']).then(result => {
console.log(result)
// ["a", "b", "p1", "p2", "c"]
})在这段代码中,我们的p1的执行是延迟了2秒的。这里如果不使用Promise.all()的话,最终顺序将与调用顺序不同。
现在我们来分析all()的实现思路:
all()方法可以通过类直接调用,因此是一个静态方法all()方法接收一个数组,数组中的值可以是普通值,也可以是MyPromise的实例对象- 返回一个新的MyPromise实例对象
- 遍历数组中的每一个值,判断值的类型。如果是普通值,直接存入一个数组;如果是MyPromise的实例对象,则调用then方法。根据执行后的状态,如果失败则调用新MyPromise实例对象中的
rejecte,如果成功则将该值存入数组 - 存入数组时进行计数,如果存入的数量达到传入数组的长度,说明调用完毕,执行
resolve并将最终结果数组作为参数返回。
实现代码:
/**
* @description: 将多个Promise实例合并为一个Promise实例
* @param {*} array Promise或者普通值
* @returns {Promise}
*/
static all (array) {
// 用于存放最终结果的数组
let result = []
// 用于计算当前已经执行完的实例的数量
let count = 0
// 最后返回的是一个Promise实例
return new MyPromise((resolve, reject) => {
/**
* @description: 将执行完毕的值加入结果数组,并根据实际情况决定是否调用resolve
* @param {*} result 存放结果的数组
* @param {*} index 要加入的索引
* @param {*} value 要加入数组的值
* @param {*} resolve Promise中的resolve
*/
function addResult (result, index, value, resolve) {
// 根据索引值,将结果堆入数组中
result[index] = value
// 执行完毕一个 count+1,如果当前值等于总长度的话说明已经执行结束了,可以直接调用resolve,说明已经成功执行完毕了
if (++count === array.length) {
// 将执行结果返回
resolve(result)
}
}
// 遍历穿入的数组,每个都执行then方法,获取到最终的结果
array.forEach((p, index) => {
// 判断p是不是MyPromise的实例,如果是的话调用then方法,不是直接将值加入数组中
if (p instanceof MyPromise) {
p.then(
// 成功时将结果直接加入数组中
value => {
addResult(result, index, value, resolve)
},
// 如果失败直接返回失败原因
reason => {
reject(reason)
}
)
}
else {
addResult(result, index, p, resolve)
}
})
})
}Promise.resolve方法的实现
关于Promise.resolve()方法的用法,可参考Promise.resolve()与Promise.reject()。
我们实现的思路主要如下:
- 该方法是一个静态方法
- 该方法接受的如果是一个值,就将该值包装为一个MyPromise的实例对象返回;如果是一个MyPromise的实例对象,则调用then方法返回。
实现代码如下:
static resolve (value) {
// 如果是MyPromise的实例,就直接返回这个实例
if (value instanceof MyPromise) return value
// 如果不是的话创建一个MyPromise实例,并返回传递的值
return new MyPromise((resolve) => {
resolve(value)
})
}finally方法的实现
关于finally方法可参考finally(),实现该方法的代码如下:
finally (callback) {
// 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
// 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
// 我们需要在回调之后拿到成功的回调,所以需要把value也return
// 失败的回调也抛出原因
// 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
return this.then(value => {
// 把callback调用之后返回的promise传递过去,并且执行promise,且在成功之后返回value
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
// 失败之后调用的then方法,然后把失败的原因返回出去。
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}测试:
function p1 () {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2 () {
return new MyPromise((resolve, reject) => {
reject('p2 reject')
})
}
p2().finally(
() => {
console.log('finally p2')
return p1()
}
).then(
value => {
console.log(value)
}, reason => {
console.log(reason)
}
)
// finally p2
// 两秒之后执行p2 rejectcatch方法的实现
关于catch方法可参考catch(),实现该方法非常简单,只需在内部调用then方法,不传递第一个回调函数即可,
实现代码如下:
catch (callback) {
return this.then(null, failCallback)
}测试如下:
function p () {
return new MyPromise((resolve, reject) => {
reject(new Error('reject'))
})
}
p()
.then(value => {
console.log(value)
})
.catch(reason => console.log(reason))完整代码
// MyPromise.js
// 定义所有状态的常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// Promise实质上就是一个类,首先创建一个Promise的类
class MyPromise {
// 实例化Promise时需要一个回调函数,该回调函数立即执行
constructor(executor) {
try {
// 在调用executor需要传递两个回调函数,分别是resolve和reject
executor(this.resolve, this.reject)
} catch (e) {
// 发生异常调用reject
this.reject(e)
}
}
// Promise 的状态
status = PENDING
// 记录成功与失败的值
value = undefined
reason = undefined
// 存储成功与失败的处理逻辑
onFulfilled = []
onRejected = []
resolve = (value) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
// 形参value表示,调用resolve时传递的参数
// 如果当前状态不是pending,就直接跳出该逻辑
if (this.status !== PENDING) return
// 将状态修改为成功
this.status = FULFILLED
// 将传入的值进行保存
this.value = value
// 如果将状态修复为成功,调用成功的回调
// this.onFulfilled && this.onFulfilled(this.value)
while (this.onFulfilled.length) {
// Array.prototype.shift() 用于删除数组第一个元素,并返回
this.onFulfilled.shift()(this.value)
}
}
reject = (reason) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
// 形参reason表示,调用reject时传递的失败的原因
// 如果当前状态不是pending,就直接跳出该逻辑
if (this.status !== PENDING) return
// 将状态修改为失败
this.status = REJECTED
// 保存失败的原因
this.reason = reason
// 如果将状态修复为失败,调用失败的回调
// this.onRejected && this.onRejected(this.reason)
while (this.onRejected.length) {
// Array.prototype.shift() 用于删除数组第一个元素,并返回
this.onRejected.shift()(this.reason)
}
}
// then方法的实现
then (onFulfilled, onRejected) {
// 如果传递函数,就是用传递的函数,否则指定一个默认值,用于参数传递
onFulfilled = onFulfilled ? onFulfilled : value => value
// 同理
onRejected = onRejected ? onRejected : reason => { throw reason }
// then 方法返回一个MyPromise实例
const promise = new MyPromise((resolve, reject) => {
// 判断当前状态,根据状态调用指定回调
if (this.status === FULFILLED) {
// 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
// 如果不变成异步的话是在函数内获取不到promise的
setTimeout(() => {
try {
// 将成功的值作为参数返回
// 保存执行回调函数的结果
const result = onFulfilled(this.value)
// 如果返回的是一个普通的值,直接调用resolve
// 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
// 失败的处理同成功处理,只是调用的回调函数不同
setTimeout(() => {
try {
const result = onRejected(this.reason)
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
// 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
// this.onFulfilled.push(onFulfilled)
// this.onRejected.push(onRejected)
this.onFulfilled.push((value) => {
setTimeout(() => {
try {
const result = onFulfilled(value)
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejected.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(reason)
resolvePromise(promise, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise
}
catch (callback) {
return this.then(null, callback)
}
finally (callback) {
// 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
// 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
// 我们需要在回调之后拿到成功的回调,所以需要把value也return
// 失败的回调也抛出原因
// 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
return this.then(value => {
// 把callback调用之后返回的promise传递过去,并且执行promise,且在成功之后返回value
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
// 失败之后调用的then方法,然后把失败的原因返回出去。
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
/**
* @description: 将多个Promise实例合并为一个Promise实例
* @param {*} array Promise或者普通值
* @returns {Promise}
*/
static all (array) {
// 用于存放最终结果的数组
let result = []
// 用于计算当前已经执行完的实例的数量
let count = 0
// 最后返回的是一个Promise实例
return new MyPromise((resolve, reject) => {
/**
* @description: 将执行完毕的值加入结果数组,并根据实际情况决定是否调用resolve
* @param {*} result 存放结果的数组
* @param {*} index 要加入的索引
* @param {*} value 要加入数组的值
* @param {*} resolve Promise中的resolve
*/
function addResult (result, index, value, resolve) {
// 根据索引值,将结果堆入数组中
result[index] = value
// 执行完毕一个 count+1,如果当前值等于总长度的话说明已经执行结束了,可以直接调用resolve,说明已经成功执行完毕了
if (++count === array.length) {
// 将执行结果返回
resolve(result)
}
}
// 遍历穿入的数组,每个都执行then方法,获取到最终的结果
array.forEach((p, index) => {
// 判断p是不是MyPromise的实例,如果是的话调用then方法,不是直接将值加入数组中
if (p instanceof MyPromise) {
p.then(
// 成功时将结果直接加入数组中
value => {
addResult(result, index, value, resolve)
},
// 如果失败直接返回失败原因
reason => {
reject(reason)
}
)
}
else {
addResult(result, index, p, resolve)
}
})
})
}
static resolve (value) {
// 如果是MyPromise的实例,就直接返回这个实例
if (value instanceof MyPromise) return value
// 如果不是的话创建一个MyPromise实例,并返回传递的值
return new MyPromise((resolve) => {
resolve(value)
})
}
}
function resolvePromise (promise, result, resolve, reject) {
// 这里修改一下该函数,如果return的Promise实例对象,也就是传入的promise===result的话,说明在promise中return的是当前promise对象。
if (promise === result) {
// 这里调用reject,并抛出一个Error
// return 是必须的,阻止程序向下执行
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断传递的result是不是MyPromise的实例对象
if (result instanceof MyPromise) {
// 说明是MyPromise的实例对象
// 调用.then方法,然后在回调函数中获取到具体的值,然后调用具体的回调
// result.then(value => resolve(value), reason => reject(reason))
// 简写
result.then(resolve, reject)
} else {
resolve(result)
}
}
module.exports = MyPromise到此这篇关于万字详解JavaScript手写一个Promise的文章就介绍到这了,更多相关JS手写Promise内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
