promise的构成和需要实现哪些东西
按照Promise/A+的标准来说,只需要Promise的then方法即可,至于怎么实现、constructor怎么写,是没有说的,所以这里使用es5
或者es6
来实现,其实不重要,如果为了兼容建议使用es5
吧,只要注意其中几个重点。
-
首先,constructor是必须的,在这里我们需要做一些初始化的动作:
- 初始化状态
- 对传入的参数(resolver)进行类型判断,容错处理
- 执行传入的resolver函数
- 定义resolve和reject函数
-
其次,then 是在每一个对象都存在的,所以这里实现在原型链上。在then函数中,我们需要做的事情有:
- 对传入的onFulfilled、onRejected进行判断并做容错处理。
- 根据当前的Promise的状态做处理,然后返回一个新的Promise。
-
然后,我们还需要一个resolvePromise函数,这个函数根据标准而来(其实不管怎样,Promise的实现中都是少不了这样的处理过程)
至于原型上的catch方法以及race、all等静态方法,暂时不处理,而且这些在我们实现了Promise的基础代码后都是很容易的事情,暂时不做处理。
这里使用 ES6的class写法来实现,没什么,只是方便而已。
Constructor
|
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
|
// 首先我们定义三种状态, 这三个状态是在整个Promise类的外层。 const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' // 定义class class MyPromise { constructor(resolver) { // 对resolver的类型进行判断 if (typeof resolver !== 'function') { throw new TypeError('Promise resolver ' + resolver + 'is not a function') } this.status = PENDING // 初始化状态为 pending this.data = null // 初始化数据变量 this.fulfilledCallbacks = [] // 当状态变成 fulfilled 时候,执行的队列函数 this.rejectedCallbacks = [] // 当状态变成 rejected 时候,执行的队列函数 resolve = (data) => { // 将回调放在下一个异步队列中执行 setTimeout(() => { // 实际resolve的执行应该是 Microtasks ,这里使用异步队列模拟 if (this.status !== PENDING) return // 因为promise的状态只能从pending -> fulfilled 或者 pending -> rejected, 所以这里做一个判断,如果不是 pending 就直接返回 this.status = FULFILLED // 修改状态 this.data = data let currentCallback // 执行所有的回调函数 for (let i = 0; i < this.fulfilledCallbacks.length; i++) { currentCallback = this.fulfilledCallbacks[i] typeof currentCallback === 'function' && currentCallback(this.data) } }, 0) } // 与 resolve同理 reject = (reason) => { setTimeout(() => { if (this.status !== PENDING) return this.status = REJECTED this.data = reason let currentCallback for (let i = 0; i < this.rejectedCallbacks.length; i++) { currentCallback = this.rejectedCallbacks[i] typeof currentCallback === 'function' && currentCallback(this.data) } }, 0) } try { resolver(resolve, reject) } catch (e) { reject(e) } } // ... }
|
这个构造函数的作用就如上所说,就是初始化状态和定义两个关键的函数(resolve、reject),创建一个MyPromise对象,const myPromise = new MyPromise(function(resolve, reject){ ... })
, function(resolve, reject){ ... }
这个函数会在构造函数的最下面的try...catch
中执行,然后将定义的resolve和reject
传入。
|
1 2 3 4 5 6
|
try { resolver(resolve, reject) // function(resolve, reject){ ... } } catch (e) { reject(e) }
|
Promise.prototype.then
在实现最关键的then
方法,由于没有个实例都是有then
,所以这个方法应该放在原型上,根据ES6
的class
的写法,直接在class
中定义,就是挂着在prototype
上的,详情可以可以看一下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
|
const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class MyPromise { constructor(resolver) { ... } // 传入两个参数,一个是成功的回调,一个是错误处理 // then(function(data) {}, function(err){}) then(onFulfilled, onRejected) { const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (val) { return val } const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason } if (this.status !== PENDING) { return p = new MyPromise((resolve, reject) => { const funcCall = this.status === FULFILLED ? realOnFulfilled : realOnRejected try { const rst = funcCall(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } }) } else { return p = new MyPromise((resolve, reject) => { // 放入回调 this.fulfilledCallbacks.push(() => { try { const rst = onFulfilled(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } }) this.rejectedCallbacks.push(() => { try { const rst = onRejected(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } }) }) } } }
|
可以看到then函数首先进行了参数兼容,然后又对状态进行了判断,再返回了一个新的Promise对象。接下来就来分析这里的代码。
-
对传入的两个参数,进行类型判断,如果不符合就使用默认的函数
|
1 2 3
|
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (val) { return val } const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason }
|
-
声明一个变量,之后用来存放新的
Promise
对象 -
状态的判断,如果执行then的时候,当前对象的状态不是 pending 状态,则说明已经决议到了 fulfilled 或者 rejected状态,那么这个时候,就可以直接执行传入的 onFulfilled和onRejected函数了
|
1 2 3 4
|
if (this.status !== PENDING) { }
|
-
promise的链式操作,所以这里必须返回一个新的MyPromise对象
|
1 2 3 4
|
return p = new MyPromise((resolve, reject) => { //注意,promise构造函数时同步执行的,所以这里的代码会在这里直接执行,只是resolve函数会放在异步队栈,等同步执行完后,再去执行,当然ES6的Promise是放在了微任务队列,与异步队列还是有点区别的,这里只是模拟。 })
|
-
判断当前的状态是 fulfilled 还是 rejected状态,来决定是执行传入的 onFulfilled还是onRejected 函数
|
1 2 3 4 5 6 7 8 9 10
|
return p = new MyPromise((resolve, reject) => { const funcCall = this.status === FULFILLED ? realOnFulfilled : realOnRejected try { const rst = funcCall(this.data) // 把当前对象的数据丢进去执行 resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } })
|
this.data
这个数据的由来是根据构造函数定义的resolve
和reject
两个函数中赋值的,当这两个函数被调用了,那么this.data
就有值了,then
方法只是把这个值传入onFulfilled或者onRejected
函数去执行,也就是交给我们来处理。 这里有一个resolvePromise(p, rst, resolve, reject)
方法,这个resolvePromise
是需要自己实现的,这个只是来判断把this.data
带入onFulfilled或者onRejected
函数执行后的返回值到底是什么类型,然后根据类型来判断该怎么改变状态和怎么执行。 -
状态的判断,如果当前的状态还是 pending 状态,那么就将
onFulfilled和onRejected
函数放在相应的队列中,等待resolve
和reject
被执行了,然后再执行这个两个队列。|
1 2 3 4 5 6 7
|
else { // 还是返回一个promise对象,链式, 记住了。 return p = new MyPromise((resolve, reject) => { ... }) }
|
-
将
onFulfilled
放入fulfilledCallbacks
队列,等待执行|
1 2 3 4 5 6 7 8 9
|
this.fulfilledCallbacks.push(() => { try { const rst = onFulfilled(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } })
|
-
将
onRejected
放入rejectedCallbacks
队列,等待执行|
1 2 3 4 5 6 7 8 9
|
this.rejectedCallbacks.push(() => { try { const rst = onRejected(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } })
|
整个then
的逻辑就大致如上了。
resolvePromise的函数的实现
先上代码
|
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
|
function resolvePromise(promise, data, resolve, reject) { let then, thenCalled if (promise === data) reject(new TypeError('Chaining cycle detected for promise!')) if (data instanceof MyPromise) { // 如果在then里面执行后,手动返回了一个Promise if (data.status === PENDING) { data.then((value) => { resolvePromise(promise, value, resolve, reject) }, reject) } else { data.then(resolve, reject) } } else if (data !== null && (typeof data === 'function' || typeof data === 'object')) { // 其他库的兼容 try { then = data.then if (typeof then === 'function') { then.call(data, (value) => { if (thenCalled) return resolvePromise(promise, value, resolve, reject) }, (reason) => { if (thenCalled) return thenCalled = true return reject(reason) }) } else { resolve(data) } } catch (error) { if (thenCalled) return thenCalled = true reject(error) } } else { resolve(data) } }
|
这里主要看这个函数最主要的就是if...else if...else
这三个分支了。
-
if (data instanceof MyPromise)
:如果传入的data
的值,是MyPromise的实例,也就是说,我们在onFulfilled
里面有手动的返回了一个MyPromise
示例,就比如下面的代码:|
1 2 3 4 5
|
p.then((rst) => { ... return new MyPromise((resolve, reject) => { ... }) // 手动的返回了一个 MyPromise })
|
在回想一下,
then
函数最后是会返回一个MyPromise
实例的,但是这里,这个函数是传入then
的第一个参数onFulfilled
, 这里的判断是如果onFulfilled
又返回了一个MyPromise
实例,不要弄混了。|
1 2 3 4 5 6
|
// 传入then的参数onFulfilled: (rst) => { ... return new MyPromise((resolve, reject) => { ... }) // 手动的返回了一个 MyPromise }
|
根据这个判断,既然
data
又是一个MyPromise
,所以就直接执行data.then
了:if (data.status === PENDING) { // 判断一下这个 `MyPromis`的状态, 如果封装的好一点,这个状态应该不能这样直接被访问,可以弄一个获取状态的get函数。 data.then((value) => { resolvePromise(promise, value, resolve, reject) // 递归调用resolvePromise,注意这里是当前这个Promise决议到了 fulfilled 状态才会去在执行 resolvePromise,这样就能链式起来了 }, reject) } else { // 如果已经决议了,那就直接把resolve和reject传进去,执行了,然后再走一遍这个MyPromise的then方法 data.then(resolve, reject) }
-
else if (data !== null && (typeof data === 'function' || typeof data === 'object'))
: 这个判断主要是为了兼容其他Promise
的库。- 尽量去执行
data
的then
函数:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
try { then = data.then if (typeof then === 'function') { then.call(data, (value) => { // 当前的 data 传入其他Promise库去执性 if (thenCalled) return resolvePromise(promise, value, resolve, reject) }, (reason) => { if (thenCalled) return thenCalled = true // 被标记过 then 已经执行过了 return reject(reason) }) } else { // 如果没有then,就直接把data放入resolve执行了 resolve(data) } } catch (error) { // reject的执行逻辑 if (thenCalled) return thenCalled = true reject(error) }
|
- 尽量去执行
-
第三种情况,其他,就直接执行
resolve
了。
catch 方法
先上代码
|
1 2 3 4 5 6 7 8
|
catch(onRejected) { const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason } let p = new Promise((resolve, reject) => { let x = realOnRejected(this.data); resolvePromise(promise2, x, resolve, reject); }); }
|
其实 catch
方法只是 reject
执行逻辑而已,这里就把 then
里面对于 reject
的逻辑拿来用就行。不过这里没有返回一个Promise了,因为已经报错了么,如果想实现finally
,这里是需要返回的。
最后
其实对于Promise关键就是这些了,理解原理,除了问题也是比较好定位的。关于Promise
容易吞掉错误的情况还是需要注意。其他的Promise上的一些静态函数all
、race
等以后再看看吧。
全部的源码
|
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
|
// myPromise.js const MyPromise = (function () { const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' function resolvePromise(promise, data, resolve, reject) { let then, thenCalled if (promise === data) reject(new TypeError('Chaining cycle detected for promise!')) if (data instanceof MyPromise) { // 如果在then里面执行后,手动返回了一个Promise if (data.status === PENDING) { data.then((value) => { resolvePromise(promise, value, resolve, reject) }, reject) } else { data.then(resolve, reject) } } else if (data !== null && (typeof data === 'function' || typeof data === 'object')) { // 其他库的兼容 try { then = data.then if (typeof then === 'function') { then.call(data, (value) => { if (thenCalled) return resolvePromise(promise, value, resolve, reject) }, (reason) => { if (thenCalled) return thenCalled = true return reject(reason) }) } else { resolve(data) } } catch (error) { if (thenCalled) return thenCalled = true reject(error) } } else { resolve(data) } } return class MyPromise { constructor(resolver) { if (typeof resolver !== 'function') { throw new TypeError('Promise resolver ' + resolver + 'is not a function') } this.status = PENDING this.fulfilledCallbacks = [] this.rejectedCallbacks = [] this.data = null const onFulfilled = (data) => { setTimeout(() => { if (this.status !== PENDING) return this.status = FULFILLED this.data = data let currentCallback for (let i = 0; i < this.fulfilledCallbacks.length; i++) { currentCallback = this.fulfilledCallbacks[i] typeof currentCallback === 'function' && currentCallback(this.data) } }, 0) } const onRejected = (reason) => { setTimeout(() => { if (this.status !== PENDING) return this.status = REJECTED this.data = reason let currentCallback for (let i = 0; i < this.rejectedCallbacks.length; i++) { currentCallback = this.rejectedCallbacks[i] typeof currentCallback === 'function' && currentCallback(this.data) } }, 0) } try { resolver(onFulfilled, onRejected) } catch (e) { onRejected(e) } } then(onFulfilled, onRejected) { let p const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (val) { return val } const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason } if (this.status !== PENDING) { // 立即执行 return p = new MyPromise((resolve, reject) => { const funcCall = this.status === FULFILLED ? realOnFulfilled : realOnRejected try { const rst = funcCall(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } }) } else { return p = new MyPromise((resolve, reject) => { // 放入回调 this.fulfilledCallbacks.push(() => { try { const rst = onFulfilled(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } }) this.rejectedCallbacks.push(() => { try { const rst = onRejected(this.data) resolvePromise(p, rst, resolve, reject) } catch (error) { reject(error) } }) }) } } catch(onRejected) { const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason } let p = new Promise((resolve, reject) => { let x = realOnRejected(this.data); resolvePromise(promise2, x, resolve, reject); }); } } })()
|