在ES6之前,对于一些异步任务的处理始终没有很好的方案可以解决,处理异步的方案可谓是十分混乱,在业务需求下异步请求的套用,就形成了回调地狱,严重影响代码的阅读性。而Promise的出现,给我们统一了规范,解决了之前处理异步任务的许多痛点,并且它友好的使用方式,使之成为了JavaScript一大重点,同时也是面试的高频问点,下面就一起来全面认识一下Promise吧。
如果我们想在一个异步请求之后,拿到请求的结果,在ES6之前我们可以怎么做呢?
比如,给定一个请求地址,希望拿到它请求成功或者失败的结果:
function request(url, successCb, failCb) { setTimeout(function() { if (url === '/aaa/bbb') { // 请求成功 let res = [1, 2, 3] successCb(res) } else { // 请求失败 let err = 'err message' failCb(err) } })}// 调用方式,从回调中拿结果request('/aaa/bbb', function(res) { console.log(res)}, function(err) { console.log(err)})将上面的情况使用Promise来实现一下:
function request(url) { return new Promise((resolve, reject) => { setTimeout(() => { if (url === '/aaa/bbb') { let res = [1, 2, 3] resolve(res) // 请求成功调用resolve } else { let err = 'err message' reject(err) // 请求失败调用reject } }) })}const p = request('/aaa/bbb')p.then(res => { console.log(res) // 拿到resolve传递过来的值}, err => { console.log(err) // 拿到reject传递过来的值})为什么Promise能够将请求的结果准确的传递到then中的回调函数中,因为Promise其核心就用三种状态来进行管控。
通过上面的案例,可以在浏览器中查看Promise分别在执行resolve和reject后的打印结果和Promise当时处于的状态:
resolve和reject都没执行:

执行resolve,请求成功:

执行reject,请求失败:

注意:在后续的对Promise的讲述过程中,都需要带着Promise的状态去理解。
executor是在创建Promise是需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数,分别就是resolve和reject。
new Promise((resolve, reject) => { console.log('我是executor中的代码,我会被立即执行~')})通常我们会在executor中确定Promise的状态,而且状态一旦被确定下来,Promise的状态就会被锁死,即Promise的状态一旦修改,就不能再次更改了。
上面聊到了resolve需要传入一个普通值,Promise的状态才会被立即锁定为fulfilled,那么如果传递的不是普通值呢?一般resolve传递以下三类值,会有不同的表现效果。
传值一:resolve传入一个普通值或普通对象,那么这个值会作为then中第一个回调的参数;
const p = new Promise((resolve, reject) => { resolve(123)})p.then(res => { console.log(res) // 123})传值二:resolve传入一个Promise,那么这个传入的Promise会决定原来Promise的状态;
传入的Promise调用的是resolve;
const newP = new Promise((resolve, reject) => { resolve(123)})const p = new Promise((resolve, reject) => { resolve(newP)})p.then(res => { console.log(res) // 123}, err => { console.log(err)})传入的Promise调用的是reject;
const newP = new Promise((resolve, reject) => { reject('err message')})const p = new Promise((resolve, reject) => { resolve(newP)})p.then(res => { console.log(res)}, err => { console.log(err) // err message})传值三:resolve传入一个特殊对象,该对象中实现了then方法,那么Promise的状态就是对象中then方法执行后的结果来决定的;
then中执行了resolve;
const obj = { then: function(resolve, reject) { resolve(123) }}const p = new Promise((resolve, reject) => { resolve(obj)})p.then(res => { console.log(res) // 123}, err => { console.log(err)})then中执行了reject;
const obj = { then: function(resolve, reject) { reject('err message') }}const p = new Promise((resolve, reject) => { resolve(obj)})p.then(res => { console.log(res)}, err => { console.log(err) // err message})Promise的实例方法,就是可以通过其实例对象进行调用的方法。
then方法是Promise实例对象上的一个方法:
Promise.prototype.then
(1)then方法接收两个参数
promise.then(res => { console.log('状态变成fulfilled回调')}, err => { console.log('状态变成rejected回调')})(2)then方法多次调用
const p = new Promise((resolve, reject) => { resolve('aaa')})p.then(res => { console.log(res) // aaa})p.then(res => { console.log(res) // aaa})p.then(res => { console.log(res) // aaa})(3)then方法中的返回值
then调用本身是有返回值的,并且它的返回值是一个Promise,所以then可以进行链式调用,但是then方法调用的返回值的状态是什么呢?主要是由其返回值决定的。
当then方法中的回调在执行时处于pending状态;
当then方法中的回调返回一个结果时处于fulfilled状态,并且会将结果作为resolve的参数;
返回一个普通的值:这个普通的值会被作为一个新Promise的resolve中的值
p.then(res => { return 123 // 相当于: /* return new Promise((resolve, reject) => { resolve(123) }) */}).then(res => { console.log(res) // 123})返回一个实现了then方法的对象:
p.then(res => { const obj = { then: function(resolve, reject) { resolve('abc') } } return obj // 相当于: /* return new Promise((resolve, reject) => { resolve(obj.then) }) */}).then(res => { console.log(res) // abc})返回一个Promise:
p.then(res => { const newP = new Promise((resolve, reject) => { resolve(123) }) return newP // 相当于: /* const newP = new Promise((resolve, reject) => { resolve(123) }) return new Promise((resolve, reject) => { resolve(newP) }) */}).then(res => { console.log(res) // 123})当then方法执行时抛出一个异常,就处于rejected状态,同样,Promise的executor在执行的时候抛出异常,Promise对应的状态也会变成rejected;
const p = new Promise((resolve, reject) => { throw new Error('err message')})p.then(res => { console.log(res)}, err => { console.log(err) // Error: err message return new Error('then err message')}).then(res => { console.log(res)}, err => { console.log(err) // Error: then err message})catch方法是Promise实例对象上的一个方法:
Promise.prototype.catch
(1)catch方法可多次调用
const p = new Promise((resolve, reject) => { reject('err message')})p.catch(err => { console.log(err) // err message})p.catch(err => { console.log(err) // err message})p.catch(err => { console.log(err) // err message})(2)catch方法的返回值
catch方法的执行返回的也是一个Promise对象,使用catch后面可以继续调用then方法或者catch方法;
如果在catch后面调用then方法,会进入到then方法的fulfilled回调函数中,因为catch返回的Promise默认是fulfilled;
p.catch(err => { return 'catch return value'}).then(res => { console.log(res) // catch return value})如果catch后续又调用了catch,那么可以抛出一个异常,就会进入后面的catch回调中;
p.catch(err => { throw new Error('catch err message')}).catch(err => { console.log(err) // Error: catch err message})(3)catch的作用
catch主要是用于捕获异常的,当executor抛出异常是,可以通过catch进行处理;
注意:当Promise的executor执行reject或者抛出异常,后续必须要有捕获异常的处理,如下代码,虽然都调用了then方法,接着后续又调用了catch方法,但是then和catch是两次独立的调用,两次调用并没有联系,所以就被认定为没有处理异常。
const p = new Promise((resolve, reject) => { reject('err message')})p.then(res => { console.log(res)})p.catch(err => { console.log(err)})
正确处理的方法为:
// 方法一:p.then(res => { console.log(res)}).catch(err => { console.log(err)})// 方法二:p.then(res => { console.log(res)}, err => { console.log(err)})finally方法是Promise实例对象上的一个方法:
Promise.prototype.finally
const p = new Promise((resolve, reject) => { resolve(123)})p.then(res => { console.log(res) // 123}).catch(err => { console.log(err)}).finally(() => { console.log('finally code') // finally code})Promise的类方法,就是直接通过Promise进行调用。
resolve方法具体有什么用呢?当我们希望将一个值转成Promise来使用,就可以通过直接调用resolve方法来实现,其效果就相当于在new一个Promise时在executor中执行了resolve方法。
resolve传入的参数类型:
参数为一个普通的值;
const p = Promise.resolve('aaaa')// 相当于:/* const p = new Promise((resolve, reject) => { resolve('aaaa') })*/console.log(p)
参数为一个实现了then方法的对象;
const p = Promise.resolve({ then: function(resolve, reject) { resolve('aaaa') }})// 相当于:/* const p = new Promise((resolve, reject) => { resolve({ then: function(resolve, reject) { resolve('aaaa') } }) })*/console.log(p)
参数为一个Promise;
const p = Promise.resolve(new Promise((resolve, reject) => { resolve('abc')}))// 相当于:/* const p = new Promise((resolve, reject) => { resolve(new Promise((resolve, reject) => { resolve('abc') })) })*/console.log(p)
reject方法和resolve的用法一致,只不过是将可以得到一个状态为rejected的Promise对象,并且reject不过传入的是什么参数,都会原封不动作为rejected状态传递到catch中。
// 1.传入普通值const p1 = Promise.reject(123)p1.then(res => { console.log(res)}).catch(err => { console.log('err:', err)})// 2.传入实现then方法对象const p2 = Promise.reject({ then: function(resolve, reject) { resolve('aaaa') }})p2.then(res => { console.log(res)}).catch(err => { console.log('err:', err)})// 3.传入Promiseconst p3 = Promise.reject(new Promise((resolve, reject) => { resolve('aaaa') }))p3.then(res => { console.log(res)}).catch(err => { console.log('err:', err)})
all方法可以接收由多个Promise对象组成的数组(准确来说是可接收一个可迭代对象),all方法调用返回的Promise状态,由所有Promise对象共同决定。
当传入的所有Promise对象的状态都为fulfilled是,all方法返回的Promise状态就为fulfilled,并且会将所有Promise对象的返回值组成一个数组;
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(111) }, 1000)})const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve(222) }, 2000)})const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve(333) }, 3000)})Promise.all([p1, p2, p3]).then(res => { console.log('res:', res) // res: [ 111, 222, 333 ]}).catch(err => { console.log('err:', err)})当传入的Promise有一个变成了rejected状态,那么就会获取第一个变成rejected状态的返回值作为all方法返回的Promise状态;
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(111) }, 1000)})const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message') }, 2000)})const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve(333) }, 3000)})Promise.all([p1, p2, p3]).then(res => { console.log('res:', res)}).catch(err => { console.log('err:', err) // err: err message})相比于all方法,allSettled方法不管传入的Promise对象的状态是fulfilled还是rejected,最终都会讲结果返回,并且返回的结果是一个数组,数组中存放着每一个Promise对应的状态status和对应的值value。
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(111) }, 1000)})const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message') }, 2000)})const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve(333) }, 3000)})Promise.allSettled([p1, p2, p3]).then(res => { console.log('res:', res)}).catch(err => { console.log('err:', err)})
race翻译为竞争,顾名思义哪一个Promise对象最先返回结果,就使用最先返回结果的Promise状态。
一下代码是p1最先有结果的,p1中执行的是resolve,所以返回的状态为fulfilled:
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(111) }, 1000)})const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message') }, 2000)})const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve(333) }, 3000)})Promise.race([p1, p2, p3]).then(res => { console.log('res:', res) // res: 111}).catch(err => { console.log('err:', err)})any方法是ES12中新增的方法,与race是类似的,any方法会等到有一个fulfilled状态的Promise,才会决定any调用返回新Promise的状态(也就是说any一定会等到有一个Promise状态为fullfilled)。
那么,如果所有的Promise对象的状态都变为了rejected呢?最终就会报一个AggregateError错误,如果想拿到所有的rejected状态的返回值,可以通过在捕获异常回调参数中的errors获取:
const p1 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message1') }, 1000)})const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message2') }, 2000)})const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message3') }, 3000)})Promise.any([p1, p2, p3]).then(res => { console.log('res:', res)}).catch(err => { console.log(err) console.log(err.errors)})
注意:any方法是ES12新增的,node版本过低的话是会报错找不到any方法的,可以在浏览器中测试。
掌握了以上Promise的用法,那么就一步步来实现一下Promise吧。
// 定义Promise的三种状态常量const PENDING_STATUS = 'pending'const FULFILLED_STATUS = 'fulfilled'const REJECTED_STATUS = 'rejected'class MyPromise { constructor(executor) { // 初始化Promise的状态为pending this.promiseStatus = PENDING_STATUS // 初始化变量,用于保存resolve和reject传入的参数值 this.value = undefined this.reason = undefined // 1.定义executor需要传入的resolve函数 const resolve = (value) => { // 只有当Promise的状态为pending,才能将状态改变fulfilled if (this.promiseStatus === PENDING_STATUS) { this.promiseStatus = FULFILLED_STATUS this.value = value console.log('调用了resolve,状态变成fulfilled啦~') } } // 2.定义executor需要传入的reject函数 const reject = (reason) => { // 只有当Promise的状态为pending,才能将状态改变为rejected if (this.promiseStatus === PENDING_STATUS) { this.promiseStatus = REJECTED_STATUS this.reason = reason console.log('调用了reject,状态变成rejected啦~') } } // 3.将定义的两个函数传入executor并调用 executor(resolve, reject) }}简单测试一下:
// 先调用resolvenew MyPromise((resolve, reject) => { resolve() reject()})// 先调用rejectnew MyPromise((resolve, reject) => { reject() resolve()})
(1)then基本实现
class MyPromise { constructor(executor) { // 初始化Promise的状态为pending this.promiseStatus = PENDING_STATUS // 初始化变量,用于保存resolve和reject传入的参数值 this.value = undefined this.reason = undefined // 1.定义executor需要传入的resolve函数 const resolve = (value) => { // 只有当Promise的状态为pending,才能将状态改变fulfilled if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = FULFILLED_STATUS this.value = value // 状态变成fulfilled就去调用onFulfilled this.onFulfilled(this.value) }) } } // 2.定义executor需要传入的reject函数 const reject = (reason) => { // 只有当Promise的状态为pending,才能将状态改变为rejected if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = REJECTED_STATUS this.reason = reason // 状态变成rejected就去调用onRejected this.onRejected(this.reason) }) } } // 3.将定义的两个函数传入executor并调用 executor(resolve, reject) } then(onFulfilled, onRejected) { // 保存fulfilled和rejected状态的回调 this.onFulfilled = onFulfilled this.onRejected = onRejected }}注意:这里将onFulfilled和onRejected的调动放在了queueMicrotask,在JavaScript中可以通过queueMicrotask使用微任务,而原Promise的then中回调的执行,也是会被放在微任务中的,为什么要放在微任务中呢?
原因:如果不使用微任务,那么在executor中执行resolve或者reject时,then方法还没被调用,onFulfilled和onRejected就都还没被赋值,所以调用时会报错,加入微任务就可以实现将onFulfilled和onRejected的调用推迟到下一次事件循环,也就是等then调用后赋值了才会执行。
简单测试一下:
const p1 = new MyPromise((resolve, reject) => { resolve('aaaa')})const p2 = new MyPromise((resolve, reject) => { reject('err message')})p1.then(res => { console.log(res) // aaaa}, err => { console.log(err)})p2.then(res => { console.log(res)}, err => { console.log(err) // err message})(2)then优化一
class MyPromise { constructor(executor) { // 初始化Promise的状态为pending this.promiseStatus = PENDING_STATUS // 初始化变量,用于保存resolve和reject传入的参数值 this.value = undefined this.reason = undefined // 初始化两个数组,分别用于保存then中对应需要执行的回调 this.onFulfilledFns = [] this.onRejectedFns = [] // 1.定义executor需要传入的resolve函数 const resolve = (value) => { // 只有当Promise的状态为pending,才能将状态改变fulfilled if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = FULFILLED_STATUS this.value = value // 状态变成fulfilled就去遍历调用onFulfilled this.onFulfilledFns.forEach(fn => { fn(this.value) }) }) } } // 2.定义executor需要传入的reject函数 const reject = (reason) => { // 只有当Promise的状态为pending,才能将状态改变为rejected if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = REJECTED_STATUS this.reason = reason // 状态变成rejected就去遍历调用onRejected this.onRejectedFns.forEach(fn => { fn(this.reason) }) }) } } // 3.将定义的两个函数传入executor并调用 executor(resolve, reject) } then(onFulfilled, onRejected) { // 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调 if (this.promiseStatus === FULFILLED_STATUS && onFulfilled) { onFulfilled(this.value) } if (this.promiseStatus === REJECTED_STATUS && onRejected) { onRejected(this.reason) } // 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行 if (this.promiseStatus === PENDING_STATUS) { // 将then中成功的回调和失败的回调分别存入数组中 this.onFulfilledFns.push(onFulfilled) this.onRejectedFns.push(onRejected) } }}简单测试一下:
const p = new MyPromise((resolve, reject) => { resolve('aaaa') reject('err message')})p.then(res => { console.log('res1:', res)}, err => { console.log('err1:', err)})p.then(res => { console.log('res2:', res)}, err => { console.log('err1:', err)})setTimeout(() => { p.then(res => { console.log('res3:', res) }, err => { console.log('err1:', err) })})
(3)then优化二
try catch判断是调用resolve还是reject;try catch去执行executor;class MyPromise { constructor(executor) { // 初始化Promise的状态为pending this.promiseStatus = PENDING_STATUS // 初始化变量,用于保存resolve和reject传入的参数值 this.value = undefined this.reason = undefined // 初始化两个数组,分别用于保存then中对应需要执行的回调 this.onFulfilledFns = [] this.onRejectedFns = [] // 1.定义executor需要传入的resolve函数 const resolve = (value) => { // 只有当Promise的状态为pending,才能将状态改变fulfilled if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = FULFILLED_STATUS this.value = value // 状态变成fulfilled就去遍历调用onFulfilled this.onFulfilledFns.forEach(fn => { fn(this.value) }) }) } } // 2.定义executor需要传入的reject函数 const reject = (reason) => { // 只有当Promise的状态为pending,才能将状态改变为rejected if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = REJECTED_STATUS this.reason = reason // 状态变成rejected就去遍历调用onRejected this.onRejectedFns.forEach(fn => { fn(this.reason) }) }) } } // 3.将定义的两个函数传入executor并调用 // 如果executor中就抛出了异常,那么直接执行reject即可 try { executor(resolve, reject) } catch (err) { reject(err) } } then(onFulfilled, onRejected) { return new MyPromise((resolve, reject) => { // 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调 if (this.promiseStatus === FULFILLED_STATUS && onFulfilled) { // 通过try catch捕获异常,没有捕获到执行resolve,捕获到执行reject try { const value = onFulfilled(this.value) resolve(value) } catch (err) { reject(err) } } if (this.promiseStatus === REJECTED_STATUS && onRejected) { try { const reason = onRejected(this.reason) resolve(reason) } catch(err) { reject(err) } } // 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行 if (this.promiseStatus === PENDING_STATUS) { // 将then中成功的回调和失败的回调分别存入数组中 // 将传入的回调外包裹一层函数,目的是为了这里能够拿到then中回调执行的结果 this.onFulfilledFns.push(() => { try { const value = onFulfilled(this.value) resolve(value) } catch(err) { reject(err) } }) this.onRejectedFns.push(() => { try { const reason = onRejected(this.reason) resolve(reason) } catch(err) { reject(err) } }) } }) }}简单测试一下:
const p = new MyPromise((resolve, reject) => { resolve('aaaa')})p.then(res => { console.log('res1:', res) return 'bbbb'}, err => { console.log('err1:', err)}).then(res => { console.log('res2:', res) throw new Error('err message')}, err => { console.log('err2:', err)}).then(res => { console.log('res3:', res)}, err => { console.log('err3:', err)})
// catch方法实现catch(onRejected) { return this.then(undefined, onRejected)}then方法改进:

简单测试一下:
const p = new MyPromise((resolve, reject) => { reject('err message')})p.then(res => { console.log(res)}).catch(err => { console.log(err) // err message})finally(onFinally) { this.then(() => { onFinally() }, () => { onFinally() }) // 也可直接简写成: // this.then(onFinally, onFinally)}then方法改进:

简单测试一下:
const p = new MyPromise((resolve, reject) => { resolve('aaaa')})p.then(res => { console.log('res:', res) // res: aaaa}).catch(err => { console.log('err:', err)}).finally(() => { console.log('我是一定会执行的!') // 我是一定会执行的!})static关键字;static resolve(value) { return new MyPromise((resolve, reject) => resolve(value))}static reject(reasion) { return new MyPromise((resolve, reject) => reject(reasion))}简单测试一下:
MyPromise.resolve('aaaa').then(res => { console.log(res) // aaaa})MyPromise.reject('bbbb').then(res => { console.log(res)}, err => { console.log(err) // bbbb})static all(promises) { return new MyPromise((resolve, reject) => { // 用于存放所有成功的返回值 const results = [] promises.forEach(promise => { promise.then(res => { results.push(res) // 当成功返回值的长度与传入promises的长度相等,就调用resolve if (results.length === promises.length) { resolve(results) } }, err => { // 一旦有一个promise变成了rejected状态,就调用reject reject(err) }) }) })}简单测试一下:
const p1 = new MyPromise(resolve => { setTimeout(() => { resolve('aaaa') }, 1000)})const p2 = new MyPromise(resolve => { setTimeout(() => { resolve('bbbb') }, 2000)})const p3 = new MyPromise(resolve => { setTimeout(() => { resolve('cccc') }, 3000)})MyPromise.all([p1, p2, p3]).then(res => { console.log(res) // [ 'aaaa', 'bbbb', 'cccc' ]}).catch(err => { console.log(err)})static allSettled(promises) { return new MyPromise((resolve, reject) => { // 用于存放所有promise的状态和返回值 const results = [] promises.forEach(promise => { promise.then(res => { results.push({ status: FULFILLED_STATUS, value: res }) // 当长度相等,调用resolve if (results.length === promises.length) { resolve(results) } }, err => { results.push({ status: REJECTED_STATUS, value: err }) // 当长度相等,调用resolve if (results.length === promises.length) { resolve(results) } }) }) })}简单测试一下:
const p1 = new MyPromise(resolve => { setTimeout(() => { resolve('aaaa') }, 1000)})const p2 = new MyPromise((resolve, reject) => { setTimeout(() => { reject('err message') }, 2000)})const p3 = new MyPromise(resolve => { setTimeout(() => { resolve('bbbb') }, 3000)})MyPromise.allSettled([p1, p2, p3]).then(res => { console.log(res)}).catch(err => { console.log(err)})
static race(promises) { return new MyPromise((resolve, reject) => { promises.forEach(promise => { // 得到状态最先改变的promise,调用对应的resolve和reject promise.then(res => { resolve(resolve) }, err => { reject(err) }) }) })}AggregateError,并且可以在AggregateError中的errors属性中获取所有错误信息;static any(promises) { return new MyPromise((resolve, reject) => { // 用于记录状态为rejected的值 const reasons = [] promises.forEach(promise => { promise.then(res => { // 当有一个promise变成fulfilled状态就调用resolve resolve(res) }, err => { reasons.push(err) // 当所有promise都是rejected就调用reject,并且传入AggregateError if (reasons.length === promises.length) { reject(new AggregateError(reasons)) } }) }) })}简单测试一下:
const p1 = new MyPromise((resolve, reject) => { setTimeout(() => { reject('err message1') }, 1000)})const p2 = new MyPromise((resolve, reject) => { setTimeout(() => { reject('err message2') }, 2000)})const p3 = new MyPromise((resolve, reject) => { setTimeout(() => { reject('err message3') }, 3000)})MyPromise.any([p1, p2, p3]).then(res => { console.log(res)}).catch(err => { console.log('err:', err) console.log(err.errors)})
上面已经对Promise的各个功能进行了实现,下面就来整理一下最终的完整版,可以将一些重复的逻辑抽取出去,比如try catch。
// 定义Promise的三种状态常量const PENDING_STATUS = 'pending'const FULFILLED_STATUS = 'fulfilled'const REJECTED_STATUS = 'rejected'// try catch逻辑抽取function tryCatchFn(execFn, value, resolve, reject) { try { const result = execFn(value) resolve(result) } catch (err) { reject(err) }}class MyPromise { constructor(executor) { // 初始化Promise的状态为pending this.promiseStatus = PENDING_STATUS // 初始化变量,用于保存resolve和reject传入的参数值 this.value = undefined this.reason = undefined // 初始化两个数组,分别用于保存then中对应需要执行的回调 this.onFulfilledFns = [] this.onRejectedFns = [] // 1.定义executor需要传入的resolve函数 const resolve = (value) => { // 只有当Promise的状态为pending,才能将状态改变fulfilled if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = FULFILLED_STATUS this.value = value // 状态变成fulfilled就去遍历调用onFulfilled this.onFulfilledFns.forEach(fn => { fn(this.value) }) }) } } // 2.定义executor需要传入的reject函数 const reject = (reason) => { // 只有当Promise的状态为pending,才能将状态改变为rejected if (this.promiseStatus === PENDING_STATUS) { // 添加微任务 queueMicrotask(() => { // 如果Promise状态不为pending,后面的代码就不再执行了 if (this.promiseStatus !== PENDING_STATUS) return this.promiseStatus = REJECTED_STATUS this.reason = reason // 状态变成rejected就去遍历调用onRejected this.onRejectedFns.forEach(fn => { fn(this.reason) }) }) } } // 3.将定义的两个函数传入executor并调用 // 如果executor中就抛出了异常,那么直接执行reject即可 try { executor(resolve, reject) } catch (err) { reject(err) } } then(onFulfilled, onRejected) { // 判断onRejected是否有值,没有值的话直接赋值一个抛出异常的方法,用于传递到下一次then中的失败回调,供catch调用 onRejected = onRejected || (err => {throw err}) // 判断onFulfilled是否有值,避免在使用catch时传入的undefined不会执行,出现断层现象 onFulfilled = onFulfilled || (value => value) return new MyPromise((resolve, reject) => { // 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调 if (this.promiseStatus === FULFILLED_STATUS) { // 通过try catch捕获异常,没有捕获到执行resolve,捕获到执行reject tryCatchFn(onFulfilled, this.value, resolve, reject) } if (this.promiseStatus === REJECTED_STATUS) { tryCatchFn(onRejected, this.reason, resolve, reject) } // 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行 if (this.promiseStatus === PENDING_STATUS) { // 将then中成功的回调和失败的回调分别存入数组中 // 将传入的回调外包裹一层函数,目的是为了这里能够拿到then中回调执行的结果 this.onFulfilledFns.push(() => { tryCatchFn(onFulfilled, this.value, resolve, reject) }) this.onRejectedFns.push(() => { tryCatchFn(onRejected, this.reason, resolve, reject) }) } }) } catch(onRejected) { return this.then(undefined, onRejected) } finally(onFinally) { this.then(onFinally, onFinally) } static resolve(value) { return new MyPromise((resolve, reject) => resolve(value)) } static reject(reasion) { return new MyPromise((resolve, reject) => reject(reasion)) } static all(promises) { return new MyPromise((resolve, reject) => { // 用于存放所有成功的返回值 const results = [] promises.forEach(promise => { promise.then(res => { results.push(res) // 当成功返回值的长度与传入promises的长度相等,就调用resolve if (results.length === promises.length) { resolve(results) } }, err => { // 一旦有一个promise变成了rejected状态,就调用reject reject(err) }) }) }) } static allSettled(promises) { return new MyPromise((resolve, reject) => { // 用于存放所有promise的状态和返回值 const results = [] promises.forEach(promise => { promise.then(res => { results.push({ status: FULFILLED_STATUS, value: res }) // 当长度相等,调用resolve if (results.length === promises.length) { resolve(results) } }, err => { results.push({ status: REJECTED_STATUS, value: err }) // 当长度相等,调用resolve if (results.length === promises.length) { resolve(results) } }) }) }) } static race(promises) { return new MyPromise((resolve, reject) => { promises.forEach(promise => { // 得到状态最先改变的promise,调用对应的resolve和reject promise.then(resolve, reject) }) }) } static any(promises) { return new MyPromise((resolve, reject) => { // 用于记录状态为rejected的值 const reasons = [] promises.forEach(promise => { promise.then(res => { // 当有一个promise变成fulfilled状态就调用resolve resolve(res) }, err => { reasons.push(err) // 当所有promise都是rejected就调用reject,并且传入AggregateError if (reasons.length === promises.length) { reject(new AggregateError(reasons)) } }) }) }) }}