来源:https://www.jianshu.com/p/43de678e918a
前言
面试中,面试官让我写一个Promise实现,我是这样写的:
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
| class MyPromise { constructor(func) { this.state = 'pendding' this.value = undefined; const resolved = (value) => { this.state = 'fulfilled'; this.value = value; } const rejected = (error) => { this.state = 'rejected'; this.value = error; } try { func(resolved, rejected); } catch(e) { rejected(e); } } then(onFulfilled, onRejected) { if (this.state === 'fulfilled') { onFulfilled(this.value); } if (this.state === 'fulfilled') { onRejected(this.value); } } }
|
然后面试官问,如果遇到中间状态怎么办?Promise
是支持多个then
方法调用的,你的这个支持么?
那一瞬间,我才感觉到,其实我对Promise
的原理理解的很片面,很多问题都没有深入核心去理解,虽然面试通过了,但是也暴露了问题,所以有必要针对Promise
的原理写一遍笔记来加深印象。
Promise
的基本结构
1 2 3
| new Promise(function(resolve, reject) { resolve('fulfilled'); })
|
构造函数Promise
必须要接受一个函数作为参数,该函数成为handle
,handle
又包含resolve
和reject
两个参数,他们是两个函数。
首先,定义一个MyPromse
的class
,接收一个handle
作为参数:
1 2 3 4 5 6 7
| class MyPromise { constructor(handle) { if (typeof handel !== 'funciton') { throw new Error('MyPromise 必须接收一个函数作为参数!'); } } }
|
Promise
的状态和值
Promise
存在三种状态:
Pendding
(进行中)
Fulfilled
(已成功)
Rejected
(已失败)
状态只能由Pendding
变更为Fulfilled
或者Rejected
,且改变状态之后,不会在发生变化,会一直保持这个状态
Promise
的值是指状态改变时传递给回调函数的值:上面的handle
函数包含的两个resolve
和reject
两个函数参数,可以用于改变Promise
的状态和传入Promise
值:
resolve
:将Promise
对象的状态从Pendding
状态改为Fulfilled
reject
:将Promise
对象的状态从Pendding
状态改为Rejected
resolve
和reject
都可以传入任意类型的值作为实参,表示Promise
对象成功和失败的值
我们继续完善上面的代码:
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
| class MyPromise { constructor(handle) { if (typeof handel !== 'funciton') { throw new Error('MyPromise 必须接收一个函数作为参数!'); } this.state = 'Pendding'; this.value = undefined; try { handle(this.resolve.bind(this), this.reject.bind(this)); } catch(err) { this.reject(err); } } resolve(value) { if (this.state !== 'Pendding') { this.state = 'Fulfilled'; this.value = value; } } reject(err) { if (this.state !== 'Pendding') { this.state = 'Rejected'; this.value = err; } } }
|
Promise
的then
方法
上面的代码,其实和我面试中写的就差不多一样了,接下来就会解答面试官提出的问题了
then
方法的参数:
Promise
对象的then
方法接收两个参数(两个参数都是可选参数,如果不是函数,则必须被忽略):
then
方法可以被同一个Promise
调用多次:
then
方法必须返回一个新的Promise
对象, 因此Promise
支持链式调用
如果onFulfilled
或者onRejected
返回一个值x
,则运行下面的Promise
解决过程:
下面是用于解释的例子:
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
| const promise1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve(); }, 1000); });
const promise2 = promise1.then(function(res) { return '这里返回的是一个普通值'; })
promise2.then(function(res) { console.log(res); }); ```
```javascript const promise1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve(); }, 1000); });
const promise2 = promise1.then(function(res) { return new Promise(function(resolve, reject) { setTimeout(funciton() { resolve('这里返回的是一个Pormise'); }, 2000); }); })
promise2.then(function(res) { console.log(res); }); ```
2. 如果`onFulfilled`或者`onRejected`抛出一个异常`e`,则`promise2`必须变为失败,并且返回一个失败的值`e`,如下:
```javascript const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) const promise2 = promise1.then(res => { throw new Error('这里抛出一个异常e') }) promise2.then(res => { console.log(res) }, err => { console.log(err) })
|
如果onFulfilled
不是函数且promise1
状态为成功,promise2
必须变为成功并返回promise1
的值,例如:
1 2 3 4 5 6 7 8 9 10 11
| const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) const promise2 = promise1.then('这里的onFulfilled本来是一个函数,但是现在是值'); promise2.then(res => { console.log(res) }, err => { console.log(err) })
|
- 如果
onRejected
不是函数且promise1
状态为失败,promise2
必须变为失败并返回promise1
失败的值,例如:
1 2 3 4 5 6 7 8 9 10 11
| const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject('fail') }, 1000) }) const promise2 = promise1.then(res => res, '这里的onFulfilled本来是一个函数,但是现在是值'); promise2.then(res => { console.log(res) }, err => { console.log(err) })
|
根据以上规则,来给MyPromise
增加一个执行队列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class MyPromise { constructor(handle) { if (typeof handel !== 'funciton') { throw new Error('MyPromise 必须接收一个函数作为参数!'); } this.state = 'Pendding'; this.value = undefined; this.fulfilledQueues = []; this.rejectedQueues = []; try { handle(this.resolve.bind(this), this.reject.bind(this)); } catch(err) { this.reject(err); } } }
|
接下来添加then
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| then(onFulfilled, onRejected){ const { state, value } = this; switch(state) { case 'Pendding': this.fulfilledQueues.push(onFulfilled); this.rejectedQueues.push(onRejected); break; case 'Fulfilled': onFullfilled(value); break; case 'Rejected': onRejected(value); break; } return new MyPromise((onFulfilledNext, onRejectedNext) => {}); }
|
那么新返回的Promise
对象什么时候改变状态?改变为那种状态呢?
根据上文中then
方法的规则,新返回的Promise
对象状态依赖于当前then
方法的回调函数执行的情况以及返回值,如:then
的参数是否为一个函数,回调函数执行是否出错,返回值是否为Pormise
对象
依照上面的规则,可以进一步完善then
方法:
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
| then(onFulfilled, onRejected) { const { state, value } = this; return new MyPromise((onFulfilledNext, onRejectedNext) => { const fulfilled = value => { try { if (typeof onFulfilledNext !== 'funciton') { onFulfilledNext(value); } else { const res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext); } else { onFulfilledNext(value); } } } catch(err) { onRejectedNext(err); } } const rejected = err => { try { if (typeof onRejected !== 'funciton') { onRejectedNext(err); } else { const res = onRejected(err); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext); } else { onRejectedNext(err); } } } catch(err) { onRejectedNext(err); } } switch(state) { case 'Pendding': this.fulfilledQueues.push(fulfilled); this.rejectedQueues.push(rejected); break; case 'Fulfilled': fulfilled(value); break; case 'Rejected': rejected(value); break; } }); }
|
然后修改resolve
和reject
方法
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
| resolve(value) { if (this.state !== 'Pendding') { return; } const run = () => { let tempFulfiiled; this.state = 'Fulfilled'; this.value = value; while(tempFulfiiled = this.fulfilledQueues.shift()) { tempFulfiiled(value); } } setTimeout(() => run(), 0); }
reject(err) { if (this.state !== 'Pendding') { return; } const run = () => { let tempRejected; this.state = 'Rejected'; this.value = err; while(tempRejected = this.rejectedQueues.shift()) { tempRejected(err); } } setTimeout(() => run(), 0); }
|
这里有一种特殊情况,即上面的代码中resolve
的方法传入的参数为一个Promise
对象时,该Promise
对象决定当前Promise
对象的状态,所以需要对resolve
方法进行修改,以便支持这个特性:
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
| resolve(value) { const run = () => { if (this.state !== 'Pendding') { return; } this.state = 'Fulfilled'; const runFulfilled = value => { let tempFulfilled; while(tempFulfilled = this.fulfilledQueues.shitf()) { tempFulfilled(value); } } const runRejected = err => { let tempRejected; while(tempRejected = this.rejectedQueues.shift()) { tempRejected(err); } }
if (value instanceof MyPromise) { val.then(value => { this.value = value; runFulfilled(value); }, err => { this.value = err; runRejected(err); }); } else { this.value = value; runFulfilled(value); } } setTimeout(() => run(), 0); }
|
以上就是基本的Promise
了
Promise
的其他方法
catch
方法:相当于调用then
方法,但是只有Rejected
状态的回调函数
1 2 3
| catch(onRejected) { return this.then(undefined, onRejected); }
|
1 2 3 4 5 6 7
| static resolve(value) { if (value instanceof MyPormise) { return value; } return new MyPromise(resolve => resolve(value)); }
|
1 2 3
| static reject (error) { return new Promise((resolve, reject) => reject(error)); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static all(list) { return MyPromise((resolve, reject) => { const values = []; let count = 0; for (let [i, p] in list.entries()) { this.resolve(p).then(res => { values[i] = res; count++; if (count === list.length) { resolve(values); } }, err => { reject(err); }) } }) }
|
1 2 3 4 5 6 7 8 9 10 11 12
| static race(list) { return MyPromise((resovle, reject) => { for (let p of list) { this.resolve(p).then(res => { resovle(res); }, err => { reject(err); }); } }); }
|
finally
方法: 不管Promise
对象最后状态如何,都会执行的操作
1 2 3 4 5 6 7
| finally(callback) { return this.then(value => { value => MyPromise.resolve(callback()).then(() => value); }, err => { err => MyPromise.reject(callback()).then(undefined, () => { throw err}; }); }
|
完整代码
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
| const isFunction = variable => typeof variable === 'function'
const PENDING = 'PENDING' const FULFILLED = 'FULFILLED' const REJECTED = 'REJECTED'
class MyPromise { constructor(handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } this._status = PENDING this._value = undefined this._fulfilledQueues = [] this._rejectedQueues = [] try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } _resolve(val) { const run = () => { if (this._status !== PENDING) return this._status = FULFILLED const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } }
if (val instanceof MyPromise) { val.then(value => { this._value = value runFulfilled(value) }, err => { this._value = err runRejected(err) }) } else { this._value = val runFulfilled(val) } } setTimeout(run, 0) } _reject(err) { if (this._status !== PENDING) return const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } setTimeout(run, 0) } then(onFulfilled, onRejected) { const { _value, _status } = this return new MyPromise((onFulfilledNext, onRejectedNext) => { let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } switch (_status) { case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) } catch (onRejected) { return this.then(undefined, onRejected) } static resolve(value) { if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) } static reject(value) { return new MyPromise((resolve, reject) => reject(value)) } static all(list) { return new MyPromise((resolve, reject) => {
let values = [] let count = 0 for (let [i, p] of list.entries()) { this.resolve(p).then(res => { values[i] = res count++ if (count === list.length) resolve(values) }, err => { reject(err) }) } }) } static race(list) { return new MyPromise((resolve, reject) => { for (let p of list) { this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) } finally(cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); } }
|