📑三、实现Promise的核心功能
1. 基础核心功能实现🏷️
(1)碎碎念
接下来我们先来实现 promise
最基础的核心功能,也就是 promise.resolve()
和 promise.reject()
这两个函数。
注意:基础功能除了构造器 constructor
意以外,其余的实现都不是绑定在原型链上的函数,将会使用箭头函数来进行实现。
(2)Promise基础功能的分析
我们先来看下 promise
的基本使用是怎么样的,具体代码如下:
/** * 01_promise的基本使用 */ const promise = new Promise(function(resolve, reject) { if (success) { resolve(value); } else { reject(error); } }); 复制代码
根据使用方式,我们可以得出 promise
有以下几个特点:
promise
是一个对象;- 当我们新建一个
promise
对象的同时,需要传进去一个回调函数; - 这个回调函数又需要接收两个回调函数
resolve
和reject
,且用这两个回调函数来作为参数,之后呢, 当调用成功时,使用resolve
回调函数,而当调用失败时,使用reject
回调函数。 resolve
和reject
这两个回调函数都将会被用来修改promise
的状态,resolve
会把pending
状态修改为fulfilled
,而reject
将会把pending
状态修改为rejected
。同时,值得注意的是,一旦状态确定后,后续所有操作的状态将不会再被更改,即不可逆。
(3)Promise基础功能的实现
我们现在来实现 promise
的基本功能,该功能含有以下几个组成要素:
- 实现
PromiseMon
的基础结构,其中包含构造函数和状态; - 实现
resolve
和reject
功能,这里先实现状态从pending
到fulfilled
或rejected
的改变,其余状态间的改变暂未实现。
具体实现代码如下:
/** * 02_promise基础功能的实现 */ // 定义pending、fulfilled和rejected三个常量 const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { // 定义Promise中的状态,默认状态为pending status = PENDING; // cb即callback,是传给promise的回调函数 constructor(cb) { // cb这个回调函数会被立即执行,作用是判断状态是否应该进行更改 cb(this.resolve, this.reject); } // 使用箭头函数的原因:箭头函数可以减少this指向造成的问题,将其绑定在promise的实例对象 // resolve回调函数 resolve = () => { // 只有当状态为pending时才能修改 if(this.status != PENDING) { return; }else{ this.status = FULFILLED; } }; // reject回调函数 reject = () => { 只有当状态为pending时才能修改 if(this.status != PENDING) { return; }else{ this.status = REJECTED; } } } // 调用resolve和reject来验证状态在确定之后不可逆 const promise1 = new PromiseMon((resolve, reject) => { resolve('resolved'); reject('rejected'); }); const promise2 = new PromiseMon((resolve, reject) => { reject('rejected'); resolve('resolved'); }); console.log(promise1.status); // fulfilled console.log(promise2.status); // rejected 复制代码
(4)thenable功能的分析
上面我们简单封装了 PromiseMon
这个函数,那现在呢,我们继续用它来实现 thenable
的功能。
大家都知道, promise
在调用了 resolve
和 reject
方法之后,就该来触发后续的 .then
或者 .catch
方法了。如果没有这两个方法的话,那么 promise
返回的数据都没啥使用的地儿,那还返回这个数据来干嘛对吧。
我们现在先来看关于 then
的基本使用操作。具体代码如下:
const fs = require('fs'); const readFile = (filename) => { const promise = new Promise(function(resolve, reject){ fs.readFile(filename, (err, data) => { if(err){ reject(err); }else{ resolve(data); } }); }); return promise; } const existedFile = readFile('./test.txt'); existedFile.then( (data) => { console.log('content: ', Buffer.from(data).toString()); }, (error) => { console.log(error); } ) 复制代码
综上代码,我们来分析 then
函数的几个特点:
then
函数接收两个参数,第一个参数在异步操作成功时进行调用,第二个则是在操作失败时调用。then
函数需要能够分析promise
的状态,分析完promise
的状态后再决定去调用成功或失败的回调函数。then
方法被定义在原型对象上:Promise.prototype.then()
。then
调用成功的回调函数时会接收一个成功的数据作为参数,同样地,当他调用失败的回调函数时,在此之前它也会接收到一个失败的原因来作为参数进行传递。then
会先接收到一个成功状态的数据,那么这个数据就用来作为参数,这个参数供给处理成功状态的回调函数进行调用;同样地,当处理失败状态时,then
会先接收到一个失败状态的数据,之后这个数据用来作为参数,这个参数供给处理失败状态的回调函数进行调用。说的这么绕,总结一下就是:接收当前状态数据→数据作为参数→拿来给回调函数调用。
(5)thenable功能的实现
我们现在来实现 thenable
的基本功能,该功能含有以下几个组成要素:
- 将在原型上实现
then
方法; - 修改原先的
resolve
函数,实现对成功状态的数据进行绑定; - 修改原先的
reject
函数,实现对失败状态的原因进行绑定。
具体实现代码如下:
/** * 03_thenable功能的实现 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; // 定义成功和失败时的值,默认都是未定义 value = undefined; reason = undefined; constructor(cb) { // cb这个回调函数会被立即执行,作用是判断状态是否应该进行更改 cb(this.resolve, this.reject); } // 修改参数,让resolve接收成功后传来的值 resolve = (value) => { if(this.status != PENDING) { return; }else{ this.status = FULFILLED; // 将成功的值赋予给value this.value = value; } }; // 修改参数,让reject接收失败后传来的原因 reject = (reason) => { if(this.status != PENDING) { return; }else{ this.status = REJECTED; // 将失败的原因赋予给reason this.reason = reason; } } /** then要接收两个回调函数,successCB这个回调函数在状态为fulfilled时使用, * 而failCB这个回调函数在状态为rejected时进行使用 */ then(successCB, failCB) { if(this.status === FULFILLED) { successCB(this.value); }else if(this.status === REJECTED) { failCB(this.reason); } } } // 测试用例 //成功状态测试 const successPromise = new PromiseMon((resolve, reject) => { resolve('successData'); reject('failData'); }); console.log('成功状态:', successPromise.status); // 成功状态:successData successPromise.then( (value) => { console.log('success:', value); // success:successData }, (reason) => { console.log('error:', reason); // 没有输出 } ) //失败状态测试 const failPromise = new PromiseMon((resolve, reject) => { reject('failData'); resolve('successData'); }); console.log('失败状态:', failPromise.status); // 失败状态:failData failPromise.then( (value) => { console.log('success:', value); // 没有输出 }, (reason) => { console.log('error:', reason); // error:failData } ) 复制代码
到这里,我们就实现了一个最基础的、且同步执行的 Promise
。接下来我们来为这个同步的 Promise
添加异步逻辑。
2. 添加异步逻辑功能实现🏷️
(1)then中添加异步逻辑功能的分析
一般来说,我们在 promise
中被调用的大部分都是异步函数,比如 setTimeout
、 setInterval
等等。所以呢,我们现在要在 then
中添加异步的功能,来实现对异步逻辑进行操作。
在上面的 then
方法中,大家定位到 if……else if……
部分,上面所写的逻辑只有对状态为 fulfilled
和 rejected
时才进行判断,而没有对状态为 pending
时进行判断。
所以,当状态为 pending
时,意味着 PromiseMon
中的异步函数还没有执行完毕。这个时候,我们需要将 succesCB
和 failCB
这两个回调函数给先存到一个变量中去,等到后续异步内容结束后再进行调用。
(2)then中添加异步逻辑功能的实现
依据上面的分析,我们来实现这个异步功能。具体代码如下:
/** * 04_添加异步逻辑功能的实现 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; // 定义两个变量,来存放成功和失败时的回调函数 successCB = undefined; failCB = undefined; constructor(cb) { cb(this.resolve, this.reject); } resolve = (value) => { if(this.status != PENDING) { return; }else{ this.status = FULFILLED; this.value = value; /** * 表达式a && 表达式b: * 计算表达式a的运算结果, * 如果为true,执行表达式b,并返回b的结果; * 如果为false,返回a的结果。 */ /** * 当successCB里面有存放成功的回调函数时,则表明this.successCB为true, * 继续判断新传来的值的状态是否为成功状态的值, * 如果是,则将新的值传给this.successCB回调函数, * 如果否,则返回原来存放着的this.success的结果 */ this.successCB && this.successCB(this.value); } }; reject = (reason) => { if(this.status != PENDING) { return; }else{ this.status = REJECTED; this.reason = reason; // 存放调用失败的回调函数 this.failCB && this.failCB(this.reason); } } /** then要接收两个回调函数,successCB这个回调函数在状态为fulfilled时使用, * 而failCB这个回调函数在状态为rejected时进行使用 */ then(successCB, failCB) { if(this.status === FULFILLED) { successCB(this.value); }else if(this.status === REJECTED) { failCB(this.reason); } // 当函数还没有执行完毕时,只能等待 else{ // 将两个回调函数的值存放起来 this.successCB = successCB; this.failCB = failCB; } } } // 测试用例 // 测试异步成功状态 const asyncPromise1 = new PromiseMon((resolve, reject) => { setTimeout(() => { resolve('asyncSuccessData'); }, 2000); }); asyncPromise1.then( (value) => { console.log('异步成功状态:', value); }, (reason) => { console.log('异步失败状态:', reason); } ); // 测试异步失败状态 const asyncPromise2 = new PromiseMon((resolve, reject) => { setTimeout(() => { reject('asyncErrorData'); }, 1000); }); asyncPromise2.then( (value) => { console.log('异步成功状态:', value); }, (reason) => { console.log('异步失败状态:', reason); } ); /** * 打印结果: * 异步失败状态: asyncErrorData * 异步成功状态: asyncSuccessData */ 复制代码
到这里,异步的功能我们也就实现啦!但是上面的 then
我们还只是实现 then
方法的一次调用,接下来我们来实现 then
方法的多次调用。
3. 实现then方法的多次调用🏷️
(1)多次调用then方法的功能分析
多次调用 then
方法分为两种情况:
- 同步调用
then
方法。同步调用then
方法相对比较简单,只要直接调用successCB
或failCB
回调函数即可。 - 异步调用
then
方法。之前的属性successCB
和failCB
两个回调函数是存放为对象形式,因此,我们需要先优化我们的存储形式。优化完成之后,将所有的回调函数全部存放在一起,等到执行完毕之后再依次调用。
(2)多次调用then方法的功能实现
依据上述的分析,我们来实现多次调用 then
方法的功能。具体代码如下:
/** * 05_多次调用then方法功能的实现 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; // 定义两个数组变量,来各自存放成功和失败时的所有回调函数 successCB = []; failCB = []; constructor(cb) { cb(this.resolve, this.reject); } resolve = (value) => { if(this.status != PENDING) { return; }else{ this.status = FULFILLED; this.value = value; // 使用 shift()方法,来弹出并返回第一个元素 while(this.successCB.length){ this.successCB.shift()(this.value); } } }; reject = (reason) => { if(this.status != PENDING) { return; }else{ this.status = REJECTED; this.reason = reason; // 使用 shift()方法,来弹出并返回第一个元素 while(this.failCB.length){ this.failCB.shift()(this.reason); } } } then(successCB, failCB) { if(this.status === FULFILLED) { successCB(this.value); }else if(this.status === REJECTED) { failCB(this.reason); }else{ // 通过push方法将回调函数的值存放到数组中 this.successCB.push(successCB); this.failCB.push(failCB); } } } // 测试用例 const multiplePromise1 = new PromiseMon((resolve, reject) => { setTimeout(() => { resolve('multiSuccessData'); }, 2000); }); multiplePromise1.then((value) => { console.log('第一次调用成功:', value); // 第一次调用成功: multiSuccessData }); multiplePromise1.then((value) => { console.log('第二次调用成功:', value); // 第二次调用成功: multiSuccessData }); /** * 打印结果: * 第一次调用成功: multiSuccessData * 第二次调用成功: multiSuccessData */ 复制代码
讲到这里,关于对此调用 then
方法的功能就实现完成了。现在,我们继续来实现关于 then
方法的链式调用。
4. 实现then方法的链式调用🏷️
(1)then方法链式调用的功能分析
我们先来对 then
方法的链式调用进行功能分析,具体如下:
- 不考虑其他功能的前提下,先完成链式调用的嵌套;
- 实现链式调用的大前提是,每一个
then
函数返回的都必须是一个Promise
对象,否则就无法衔接地去使用then
函数。 - 因此,首先我们需要在
then
函数中新建一个Promise
对象,之后呢,在新建的promise
对象里面,去处理内部使用的resolve
和reject
所返回的值,最终then
函数也就返回了promise
对象。
(2)then方法链式调用的功能实现
依据上面的功能分析,我们来实现 then
的链式调用功能。具体代码如下:
/** * 06_then的链式调用功能的实现 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; // 定义两个数组变量,来各自存放成功和失败时的所有回调函数 successCB = []; failCB = []; constructor(cb) { cb(this.resolve, this.reject); } resolve = (value) => { if(this.status != PENDING) { return; }else{ this.status = FULFILLED; this.value = value; while(this.successCB.length){ this.successCB.shift()(this.value); } } }; reject = (reason) => { if(this.status != PENDING) { return; }else{ this.status = REJECTED; this.reason = reason; while(this.failCB.length){ this.failCB.shift()(this.reason); } } } then(successCB, failCB) { // /** * 新建一个promise对象,来给下一个then使用。 * 三种状态: * ①promise对象执行成功,调用resolve; * ②promise对象执行失败,则调用reject; * ③promise对象还未执行,将回调函数推入准备好的数组中。 */ const thenablePromise = new PromiseMon((resolve, reject) => { if(this.status === FULFILLED) { const thenableValue = successCB(this.value); // 判断返回的值是否是promise对象 resolvePromise(thenableValue, resolve, reject); }else if(this.status === REJECTED) { const thenableReason = failCB(this.reason); // 判断返回的值是否是promise对象 resolvePromise(thenableReason, resolve, reject); }else{ // 通过箭头函数的方式将回调函数的值存放进数组中 this.successCB.push(() => { const thenableValue = successCB(this.value); resolvePromise(thenableValue, resolve, reject); }); this.failCB.push(() => { const thenableReason = failCB(this.reason); resolvePromise(thenableReason, resolve, reject); }); } }); return thenablePromise; } } /** * 判断传进来的thenablePromise是否是promise对象, * 如果是,则调用then函数来处理;如果否,则直接返回值。 */ const resolvePromise = (thenablePromise, resolve, reject) => { // 判断是否是一个promise对象 if(thenablePromise instanceof PromiseMon) { thenablePromise.then(resolve, reject); } else { // 如果不是promise对象,则直接返回值 resolve(thenablePromise); } } // 测试用例 // 测试链式调用成功状态 const thenablePromise = new PromiseMon((resolve, reject) => { resolve('thenableSuccessData'); }); const otherPromise = () => { return new PromiseMon((resolve, reject) => { setTimeout(() => { resolve('otherPromise'); }, 2000); }); } const anotherPromise = () => { return new PromiseMon((resolve, reject) => { setTimeout(() => { resolve('anotherPromise'); }); }); } thenablePromise .then((value) => { console.log('第一次链式调用成功:', value, new Date()); // 第一次调用成功: thenableSuccessData // return的结果是为了给下一个then使用 return otherPromise(); }) .then((value) => { console.log('第二次链式调用成功:', value, new Date()); // 第二次调用成功: otherPromise return anotherPromise(); }) .then((value) => { console.log('第三次链式调用成功:', value, new Date()); // 第三次调用成功: anotherPromise }) /** * 打印结果: * 第一次链式调用成功: thenableSuccessData 2021-08-04T11:13:25.868Z * 第二次链式调用成功: otherPromise 2021-08-04T11:13:25.877Z * 第三次链式调用成功: anotherPromise 2021-08-04T11:13:25.878Z */ 复制代码
至此,我们就完成了 promise
的链式调用。
(3)链式调用的自我检测
有时候我们有可能在调用 promise
时,会陷入自我调用的境地。也就是无限的循环嵌套和无限的自我调用。比如下面这种情况:
const promise1 = new Promise((resolve, reject) => { resolve('success'); }); // 无限循环嵌套,无限自我调用 // 当运行时控制台会抛出异常 const promise2 = promise1.then((val) => { return promise2; }); // 打印结果: // TypeError: Chaining cycle detected for promise #<Promise> 复制代码
因此,现在我们要做的是,在 resolvePromise
函数中新增一个参数,这个参数就是当前所创造的 promise
对象。之后判断两个 promise
对象是否相等,如果相等,那么就抛出异常。依据这个逻辑,我们来修改上面链式调用的功能代码,达到禁止自我调用的闭环。具体代码如下:
/** * 07_链式调用的自我检测 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; successCB = []; failCB = []; //此处省略constructor代码 //此处省略resolve代码 //此处省略reject代码 then(successCB, failCB) { const thenablePromise = new PromiseMon((resolve, reject) => { /** * 利用setTimeout是异步函数的特性, * 这样setTimeout里面的内容会在同步函数执行完之后才会进行, * 所以,当resolvePromise在执行的时候,thenablePromise就已经被实例化了, * 使得resolvePromise顺利的调用thenablePromise */ if(this.status === FULFILLED) { setTimeout(() => { const thenableValue = successCB(this.value); resolvePromise(thenablePromise, thenableValue, resolve, reject); }, 0); }else if(this.status === REJECTED) { setTimeout(() => { const thenableReason = failCB(this.reason); resolvePromise(thenablePromise, thenableReason, resolve, reject); }, 0); }else { this.successCB.push(() => { setTimeout(() => { const thenableValue = successCB(this.value); resolvePromise(thenablePromise, thenableValue, resolve, reject); }, 0); }); this.failCB.push(() => { setTimeout(() => { const thenableReason = failCB(this.reason); resolvePromise(thenablePromise, thenableReason, resolve, reject); }, 0); }); } }); return thenablePromise; } const resolvePromise = (createPromise, thenablePromise, resolve, reject) => { if(createPromise === thenablePromise) { return reject(new TypeError('出现循环调用的情况 Chaning cycle detected')) }else if(thenablePromise instanceof PromiseMon) { thenablePromise.then(resolve, reject); } else { resolve(thenablePromise); } } // 测试用例 // 测试自我调用 const thenablePromise = new PromiseMon((resolve, reject) => { resolve('chainningData'); }); const chainingPromise = thenablePromise.then((value) => { console.log('数据调用成功', value, new Date()); return chainingPromise; }) //会报错,出现循环调用 chainingPromise .then( (value) => { console.log('执行操作成功', value, new Date()); }, (reason) => { console.log('执行操作失败', reason, new Date()); } ); /* 打印结果: 数据调用成功 chainningData 2021-08-04T11:28:39.984Z 执行操作失败 TypeError: 出现循环调用的情况 Chaning cycle detected */ 复制代码
至此,我们完成了链式调用的自我检测。
5. promise的错误处理🏷️
(1)错误处理场景
到这里,我们对 pormise
的 then
方法基本实现的差不多。但是还有一个很重要但是又很容易被我们疏忽的问题就是,错误处理。现在,我们来分析一下可能会出现异常的常见场景:
- 构造器中的回调函数
cb
,需进行try/catch
处理; then
函数中的错误处理,需要对同步函数和异步函数进行try/catch
处理。
(2)错误处理功能实现
依据上面的场景分析,我们来实现 promise
的错误处理功能。具体代码如下:
/** * 08_promise的错误处理 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; successCB = []; failCB = []; constructor(cb) { try { cb(this.resolve, this.reject); } catch (err) { this.reject('err in cb'); } } //此处省略resolve代码 //此处省略reject代码 then(successCB, failCB) { const thenablePromise = new PromiseMon((resolve, reject) => { // 给setTimeout这个异步函数添加try/catch if(this.status === FULFILLED) { setTimeout(() => { try { const thenableValue = successCB(this.value); resolvePromise(thenablePromise, thenableValue, resolve, reject); } catch (err) { reject(err); } }, 0); }else if(this.status === REJECTED) { setTimeout(() => { try { const thenableReason = failCB(this.reason); resolvePromise(thenablePromise, thenableReason, resolve, reject); } catch (err) { reject(err); } }, 0); }else { // resolvePromise 同时要加到successCB和failCB中进行处理 this.successCB.push(() => { setTimeout(() => { try { const thenableValue = successCB(this.value); resolvePromise(thenablePromise, thenableValue, resolve, reject); } catch (err) { reject(err); } }, 0); }); this.failCB.push(() => { setTimeout(() => { try { const thenableReason = failCB(this.reason); resolvePromise(thenablePromise, thenableReason, resolve, reject); } catch (err) { reject(err); } }, 0); }); } }); return thenablePromise; } // 此处省略resolvePromise代码 // 测试用例 const promise = new PromiseMon((resolve, reject) => { setTimeout(() => { resolve('successData'); }); }); promise .then( (value) => { console.log(value); // (1) 打印 'successData' throw new Error('error'); // (2) 抛出异常 }, (reason) => { console.log(reason); return 'fail in then'; } ) .then( (value) => { console.log(value); }, (reason) => { console.log(reason); return 'callback fail'; // 抛出异常后返回 'callback fail' 给下面的then方面调用 } ) .then( (value) => { console.log(value); }, (reason) => { console.log(reason); // (3)上面传来callback fail,因此打印 'callback fail' } ); /* 打印结果: successData Error: error callback fail */ 复制代码
大家可以看到,通过 try/catch
的方式,对遇到的错误进行处理,并且最终抛出异常以及返回 reason
的值。
6. 实现then方法的参数可选🏷️
(1)参数可选实现思路
大家可以发现,上面我们在调用 then
方法的时候,一直都是需要进行参数传递的,这样看起来好像还不是特别友好。因此呢,我们现在来实现这个功能,让 then
方法的参数可以有传或者不传这 2
种操作。实现思路也比较简单,就是在 then
函数中判断是否传入参数,如果没有的话,则返回原来的 value
就好了。类似于下面这种形式:
promise .then((val) => val) // 这样使用箭头函数表明直接返回 value .then((val) => val) .then((val) => val) .then((val) => { console.log(val); // 200 }); 复制代码
(2)参数可选功能实现
依据上面的是实现思路,接下来我们来实现这个功能。具体代码如下:
下面我们对 then
函数进行改造:
then(successCB, failCB) { successCB = successCB ? successCB : (value) => value; failCB = failCB ? failCB : (reason) => { throw reason; }; } 复制代码
来用两组测试用例进行测试:
// 测试用例 // 成功状态下的用例 const successpromise = new PromiseMon((resolve, reject) => { resolve(100); }); successpromise .then() .then() .then() .then((val) => { console.log(val); // 100 }); // 失败状态下的用例 const failPromise = new PromiseMon((resolve, reject) => { reject(200); }); failPromise .then() .then() .then() .then( (val) => {}, (reason) => { console.log(reason); // 200 } ); /** * 打印结果: * 100 * 200 */ 复制代码
大家可以看到,不管是 resolve
还是 reject
状态,都一一完成了对参数可选功能的实现。
7. 实现Promise.all🏷️
(1)Promise.all功能分析
上面在讲 Promise
异步方案的时候就已经讲过 promise.all
和 promise.race
方法。现在,我们来梳理下实现思路:
Promise.all
是一个静态方法,它接收一个 Promise 数组 作为参数。Promise.all
的特点在于,它可以按照顺序去获取所有调用的异步函数。js
的关键字static
将会把对应的变量或函数绑定到class类上,而不是绑定在 **prototype
原型**上。
(2)Promise.all功能实现
依据实现的这个逻辑,来实现 promise.all
这个功能。具体代码如下:
/** * 10_promise.all功能的实现 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; successCB = []; failCB = []; //省略constructor、resolve、reject和then方法的代码 static all(arr){ const results = []; let index = 0; return new PromiseMon((resolve, reject) => { // 添加数据的逻辑,把指定的数据添加到数组的对应位置上 const addData = (idx, val) => { results[idx] = val; index++; // 进行这一步判断的目的:为了等待异步操作完成 if(index === arr.length) { resolve(results); } } // 对数组进行循环,获取所有的数据 arr.forEach((cur, index) => { // 如果传进来的值是一个promise对象 if(cur instanceof PromiseMon){ cur.then( (value) => addData(index, value), (reason) => reject(reason) ) } // 如果传进来的是普通值,而非promise对象 else { addData(index, cur); } }); }); } } // 此处省略resolvePromise代码 // 测试用例 const promise = () => { return new PromiseMon((resolve, reject) => { resolve(100); }); } const promise2 = () => { return new PromiseMon((resolve, reject) => { setTimeout(() => { resolve(200); }, 1000); }); } //因为使用static关键字,所以可以直接在类上面进行调用 PromiseMon.all(['a', 'b', promise(), promise2(), 'c']).then((res) => { console.log(res); // [ 'a', 'b', 100, 200, 'c' ] }) 复制代码
大家可以看到,即使 promise2
是异步函数,但最终也正常的显示在数组当中,且按序的一一进行打印。到此,也就说明 promise.all
成功实现啦!
同时, promise.race
也是按照这个模式去实现,这里不再进行讲解~
8. 实现Promise.resolve🏷️
(1)Promise.resolve功能分析
我们先来梳理下 promise.resolve
的实现思路:
- 如果参数是一个
promise
实例,那么promise.resolve()
将不做任何修改,原封不动地返回这个实例。 - 参数不是
promise
实例,或根本不是一个对象,则Promise.resolve()
方法返回一个新的promise
对象,此时状态为fulfilled
。 - 不带有任何参数,则直接返回一个
fulfilled
状态的promise
对象。
(2)Promise.resolve功能实现
依据以上的功能分析,来实现 promise.resolve
这个功能。具体代码如下:
/** * 11_promise.resolve功能的实现 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class PromiseMon { status = PENDING; value = undefined; reason = undefined; successCB = []; failCB = []; //省略constructor、resolve、reject和then方法的代码 static resolve(value){ // 传进来的是一个promise对象,原封不动返回 if(value instanceof PromiseMon) { return value; } // 传进来的不是promise对象,将其作为参数返回一个promise else { return new PromiseMon((resolve, reject) => { resolve(value); }) } } } // 此处省略resolvePromise代码 // 测试用例 const promise = () => { return new PromiseMon((resolve, reject) => { resolve(100); }); } // 1.参数是一个promise实例,那么不做任何修改,原封不动地返回这个实例 PromiseMon.resolve(promise).then((res) => { console.log(res); // 100 }) /** * 2.参数不是具有then方法的对象,或根本就不是对象, * 则Promise.resolve()方法返回一个新的promise对象,状态为fulfilled */ PromiseMon.resolve(200).then((res) => { console.log(res); // 200 }) /** * 3.不带有任何参数,则直接返回一个resolved状态的promise对象 */ PromiseMon.resolve().then(function () { console.log('two'); // two }); 复制代码
大家可以看到,依据我们所罗列的三种情况, promise.resolve
的功能也一一实现啦!
同时, promise.reject
也是按照这个模式去实现,这里不再进行讲解~
📋四、温故而知新
最后,我们再用一张思维导图来回顾本文的知识点。详情见下图👇
📝五、结束语
写到这里的时候,发现我已经花了整整三天的时间,大约接近34h+在 promise
这个知识上,好在最终算是对 promise
核心功能的的实现有一个较为满意的结果。
可能也是第一次这么细致的去啃一个知识,所以在学习过程中遇到很多以前没踩过的坑,中间过程中对问题进行详细记录并尝试解决,慢慢的就完善了一个新的知识体系。