前面已经写过一篇文章来理解Promise
及其使用方法,这里尝试来模拟Promise
。
要求
根据Promise/A+原理,有以下几个点需要遵守:
Promise
有一个Promise States
,其有三种状态:
pending
:初始状态,可以转化为fulfilled
,rejected
。
fulfilled
:
- 需要一个
value
作为结果。
- 不能转化为其他状态。
rejected
:
- 需要一个
reason
作为一个原因。
- 不能转化为其他状态。
必须要一个then
方法,接受两个参数
onFulfilled
:当操作成功时发生的回调。
onRejected
:当操作失败时发生的回调。
一旦promise
完成,每一次调用then
方法,得到的结果必须是相同的。
Promise
返回的必须也是一个promise
。
开发
实现1,2功能
首先我们实现第一个和第二个特点:
基本思路如下:
一个Promise
首先要有一个status
,表示当前的状态,其有三种状态:
PENDING
:表示当前Promise
未完成,此时的回调函数会加入相应的队列
RESOLVED
:表示当前Promise
已经完成,且时resolve
状态,直接调用成功的函数。
REJECTED
:表示当前Promise
已经完成,且时rejected
状态,直接调用失败的函数。
然后我们要定义两个值:
value
:用于resolve
时的返回值。
reason
:用于rejected
时的返回值。
上面说到当Promise
状态为PENDING
时,我们会把回调函数放到相应的队列,所以我们还会定义两个队列
resolvedCallQueue
:存放成功的回调函数
rejectedCallQueue
:存放失败的回调函数
然后我们定义Promise
的resolve
和rejected
函数,在这两个函数中,我们会改变Promise
的状态并且给value
或者reason
赋值,最后执行相应的函数队列。
最后我们会定义Promise
的then
方法。如同上面对于状态的定义,我们会根据当前Promise
的状态来处理then
方法传来的回调函数。
下面用ES6
的class
来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| class Promise { constructor(executer) { this.status_enum = { PENDING: 'PENDING', RESOLVED: 'RESOLVED', REJECTED: 'REJECTED', }; this.value = undefined; this.reason = undefined; this.resolvedCallQueue = []; this.rejectedCallQueue = []; this.status = this.status_enum.PENDING; let resolve = (value) => { if (this.status === this.status_enum.PENDING) { this.status = this.status_enum.RESOLVED; this.value = value; for (let i = 0; i < this.resolvedCallQueue.length; i++) { this.resolvedCallQueue[i](this.value); } } }; let reject = (reason) => { if (this.status === this.status_enum.PENDING) { this.status = this.status_enum.REJECTED; this.reason = reason; for (let i = 0; i < rejectedCallQueue.length; i++) { this.rejectedCallQueue[i](this.reason); } } }; try { executer(resolve, reject); } catch (e) { console.log('错误', e); reject(e); } } then(onfulfilled, onrejected) { if(!onfulfilled instanceof Function || !onrejected instanceof Function){ throw new Error('Uncaught TypeError: Promise resolver is not a function') } if (this.status === this.status_enum.RESOLVED) { onfulfilled(this.value); } if (this.status === this.status_enum.REJECTED) { onrejected(this.reason); } if (this.status === this.status_enum.PENDING) { this.resolvedCallQueue.push(onfulfilled); this.rejectedCallQueue.push(onrejected); } } }
|
下面用原型实现(基本原理一样,因为class
本质也是原型链的语法糖):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| function Promise(executer) { this.status_enum = { PENDING: 'PENDING', RESOLVED: 'RESOLVED', REJECTED: 'REJECTED', }; this.value = undefined; this.reason = undefined; this.resolvedCallQueue = []; this.rejectedCallQueue = []; this.status = this.status_enum.PENDING; let resolve = (value) => { if (this.status === this.status_enum.PENDING) { this.status = this.status_enum.RESOLVED; this.value = value; for (let i = 0; i < this.resolvedCallQueue.length; i++) { this.resolvedCallQueue[i](this.value); } } }; let reject = (reason) => { if (this.status === this.status_enum.PENDING) { this.status = this.status_enum.REJECTED; this.reason = reason; for (let i = 0; i < rejectedCallQueue.length; i++) { this.rejectedCallQueue[i](this.reason); } } }; try { executer(resolve, reject); } catch (e) { console.log('错误', e); reject(e); } } Promise2.prototype.then = function then(onfulfilled, onrejected) { if (this.status === this.status_enum.RESOLVED) { onfulfilled(this.value); } if (this.status === this.status_enum.REJECTED) { onrejected(this.reason); } if (this.status === this.status_enum.PENDING) { this.resolvedCallQueue.push(onfulfilled); this.rejectedCallQueue.push(onrejected); } };
|
这样,Promise的基本功能就已经实现,简单测试如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let p = new Promise((resolve, reject) => { setTimeout(() => { console.log('2秒!!!'); resolve(111); }, 2000); }); p.then((res) => { console.log('p1'); console.log(res); }); p.then((res) => { console.log('p2'); console.log(res); });
|
结果:
实现1,2,3功能
前面我们基本实现了Promise
的基本功能,但是还是一个问题是:Promise
必须返回一个Promise
,上面的代码并不满足这一点。
所谓我们为了实现返回的都是Promise
,我们需要重写then
方法,使之返回的是一个Promise,这时,我们还是要根据当前Promise
的状态来分开来处理。
但是其中心点是,必须持续执行Promise
,直到其返回值不是一个thenable
对象或方法,所以这是一个递归的过程。所以我们定义了一个函数cycleResolve
,其接受四个参数:
newPromise
:新建的被用于返回的Promise
target
:回调函数执行的获得结果(可能仍然是一个Promise
)
resolve
:新Promise
的resolve
函数
reject
:新Promise
的reject
函数
该函数的功能是:判定返回值target
是不是一个thenable
对象(包括Promise
),如果是,继续执行其then
方法。经过这个过程,知道返回值不是thenable
对象,然后将其值resolve
出去。
注意:
- 只有
target
是一个thenable
对象并且其then
属性是一个函数时,才会调用其then
方法,否则会直接将target
resolve出去。
- 一旦遇到错误,都会直接
reject(e)
then
方法中必须使用setTimeout
来使内部的操作成为宏任务,在下一个tick执行。这样才能拿到newPromise
,否则会出现未初始化错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| class Promise { constructor(executer) { this.status_enum = { PENDING: 'PENDING', RESOLVED: 'RESOLVED', REJECTED: 'REJECTED', }; this.value = undefined; this.reason = undefined; this.resolvedCallQueue = []; this.rejectedCallQueue = []; this.status = this.status_enum.PENDING; this.resolve = (value) => { if (this.status === this.status_enum.PENDING) { this.status = this.status_enum.RESOLVED; this.value = value; for (let i = 0; i < this.resolvedCallQueue.length; i++) { this.resolvedCallQueue[i](this.value); } } }; this.reject = (reason) => { if (this.status === this.status_enum.PENDING) { this.status = this.status_enum.REJECTED; this.reason = reason; for (let i = 0; i < this.rejectedCallQueue.length; i++) { this.rejectedCallQueue[i](this.reason); } } }; try { executer(this.resolve, this.reject); } catch (e) { console.log('错误', e); this.reject(e); } } then(onfulfilled, onrejected) { let newPromise = new Promise((resolve, reject) => { if (this.status === this.status_enum.RESOLVED) { setTimeout(() => { try { let target = onfulfilled(this.value); cycleResolve(newPromise, target, resolve, reject); } catch (e) { console.log(e); reject(e); } }, 0); } if (this.status === this.status_enum.REJECTED) { setTimeout(() => { try { let target = onrejected(this.reason); cycleResolve(newPromise, target, resolve, reject); } catch (e) { console.log(e); reject(e); } }); } if (this.status === this.status_enum.PENDING) { this.resolvedCallQueue.push(() => { setTimeout(() => { try { let target = onfulfilled(this.value); cycleResolve(newPromise, target, resolve, reject); } catch (e) { console.log(e); reject(e); } }, 0); }); this.rejectedCallQueue.push(() => { setTimeout(() => { try { let target = onrejected(this.reason); cycleResolve(newPromise, target, resolve, reject); } catch (e) { console.log(e); reject(e); } }, 0); }); } }); return newPromise; }
function cycleResolve(newPromise, target, resolve, reject) { if (newPromise === target) { return reject(new TypeError('循环调用')); } if ( target !== null && (typeof target === 'object' || typeof target === 'function') ) { try { let then = target.then; if (typeof then === 'function') { then.call( target, (newTarget) => { resolvePromise(newPromise, newTarget, resolve, reject); }, (e) => { reject(e); } ); } else { resolve(target); } } catch (e) { reject(e); } } else { resolve(target); } }
|
至此,Promise
的基本功能都已经完成,接下来我们完成剩下的一些细枝末节的东西,包括:
- 实现
resolve
方法
- 实现
reject
方法
- 实现
catch
方法。
- 实现
finally
方法。
- 实现
Promise
的其他方法:
Promise.all([p1, p2, p3])
Promise.race([p1, p2, p3])
Promise.allSettled(p1, p2, p3)
Promise.any(p1, p2, p3)
注意:
- 各个函数的功能不再赘述,具体查看Promise理解
- 下面的代码就只写对应部分,多余部分不再进行赘述。
实现catch方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| catch(onrejected) { const newPromise = new Promise3((undefined, reject) => { if (this.status === this.status_enum.REJECTED) { setTimeout(() => { try { let target = onrejected(this.reason); cycleReject(newPromise, target, reject); } catch (e) { console.log(e) reject(e); } }, 0); } if (this.status === this.status_enum.PENDING) { this.rejectedCallQueue.push(() => { setTimeout(() => { try { let target = onrejected(this.reason); cycleReject(newPromise, target, reject); } catch (e) { reject(e); } }, 0); }); } }); } }
function cycleReject(newPromise, target, reject) { if (newPromise === target) { return reject(new TypeError('循环调用')); } if ( target !== null && (typeof target === 'object' || typeof target === 'function') ) { try { let then = target.then; if (typeof then === 'function') { then.call( target, (newTarget) => { resolvePromise(newPromise, newTarget, reject); }, (e) => { reject(e); } ); } } catch (e) { reject(e); } }else{ reject(target) } }
|
其实这里的操作与then
方法逻辑基本一致,唯一不同的是,我们只需要捕捉reject
,不捕捉resolve
。
实现finally
Promise
的finally
的方法无论promise
的结果是成功还是失败,都会执行,并且返回该promise
。所以实现很简单。执行其promise
的then
方法来获取该promise
的结果。
1 2 3 4
| finally(callback) { callback(); return this.then(() => {}, () => {}); }
|
实现Promise.resolve
方法(下面的方法是直接作为class的静态函数成员的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static resolve(val) { if(val instanceof Promise3){ return val }else if(!val){ return new Promise3((resolve, reject) => {resolve()}) }else if(val && !val.then instanceof Function){ return new Promise3((resolve) => { resolve(val); }); }else if(val && val.then instanceof Function){ return new Promise3(val.then) } }
|
实现Promise.reject
方法
1 2 3 4 5 6
| static reject(val){ return new Promise((resolve,reject)=>{ reject(val); }) }
|
实现Promise.all([p1, p2, p3])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static all(arr) { let res = [] return new Promise((resolve, reject) => { for(let i = 0, len = arr.length; i < len; i++){ arr[i].then((v) => { res.push(v) if(res.length === arr.length){ return resolve(res) } }, (e) => { return reject(e) }) } }) }
|
实现Promise.race([p1, p2, p3])
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static race(arr) { return new Promise((resolve, reject) => { for (let i = 0, len = arr.length; i < len; i++) { arr[i].then( (v) => { return resolve(v) }, (e) => { return reject(e) } ); } }); }
|
实现Promise.allSettled([p1, p2, p3])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static allSettled(arr) { let res = []; let count = 0; return new Promise((resolve, reject) => { for (let i = 0, len = arr.length; i < len; i++) { arr[i].then( (v) => { res.push({ status: 'fulfilled', value: v, }); }, (e) => { res.push({ status: 'rejected', reason: e, }); } ).finally(() => { ++count === arr.length && resolve(res) }); } }); }
|
实现Promise.any(p1, p2, p3)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static any(arr) { let res = []; return new Promise((resolve, reject) => { for (let i = 0, len = arr.length; i < len; i++) { arr[i].then( (v) => { return resolve(v); }, (e) => { res.push(e); res.length === arr.length && reject(res); } ); } }); } }
|
最后
本人能力有限,可能会出现错误,谅解。