这篇文章主要介绍“怎么使用JavaScript手写一个Promise”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么使用JavaScript手写一个Promise”文章能帮助大家解决问题。
首先我们从使用的角度来分析一下Promise,然后编写一个最简单版本的Promise。
Promise就是一个类,在执行这个类的时候,需要传递一个执行器(回调函数)进去,执行器会立即执行。
Promise中的状态分为三个,分别是:
pending→等待
fulfilled→成功
rejected→失败
状态的切换只有两种,分别是:
pending→fulfilled
pending→rejected
一旦状态发生改变,就不会再次改变:
执行器中的两个参数,分别是resolve和reject,其实就是两个回调函数,调用resolve是从pending状态到fulfilled,调用reject是从状态pending到rejected。传递给这两个回调函数的参数会作为成功或失败的值。
Promise的实例对象具有一个then方法,该方法接受两个回调函数,分别来处理成功与失败的状态,then方法内部会进行判断,然后根据当前状态确定调用的回调函数。then方法应该是被定义在原型对象中的。
then的回调函数中都包含一个值,如果是成功,表示成功后返回的值;如果是失败就表示失败的原因。
根据我们上面的分析,写出如下代码:
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将毫无用处,
例如下面这段代码:
const MyPromise = require('./myPromise') let promise = new MyPromise((resolve, reject) => { setTimeout(resolve, 2000, '成功') }) promise.then(value => { console.log(value); }, reason => { console.log(reason); })
这段代码2000ms后没有任何输出,为了解决这个问题我们将对自己编写的类进行如下操作:
创建两个实例方法用于存储我们传入的成功与失败的处理逻辑。
为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)
表示如果该属性存在就调用这个方法。
现在我们重新执行一开始上面那一段代码,2s后会成功输出成功
。
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的链式调用,主要解决两个问题:
返回的是一个新的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 = MyPromise
在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
存在。
到目前为止我们现实的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的第二个函数中,实现的方式也是通过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的执行过程中遇到异常 */
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 }
关于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
的执行是延迟了2s的,这里如果不使用Promise.all()
的话最终顺序是与我们调用不同的。
现在我们来分析一下all()的实现思路:
all()
方法可以通过类直接调用,所以是一个静态方法
all()
方法接收一个数组,数组中的值可以是一个普通值,也可以是一个MyPromise的实例对象
return一个新的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.reject()
。
我们实现的思路主要如下:
该方法是一个静态方法
该方法接受的如果是一个值就将该值包装为一个MyPromise的实例对象返回,如果是一个MyPromise的实例对象,调用then方法返回。
实现代码如下:
static resolve (value) { // 如果是MyPromise的实例,就直接返回这个实例 if (value instanceof MyPromise) return value // 如果不是的话创建一个MyPromise实例,并返回传递的值 return new MyPromise((resolve) => { resolve(value) }) }
关于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 reject
关于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”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。