动手实现Promise
# # 动手实现Promise
回调函数表达异步和并发有两个主要缺陷:缺乏顺序性和可信任性
。 Promise封装了依赖时间的状态--等待底层值的完成或拒绝,所以Promise本身与时间无关。因此Promise可以按照可预测的方式组成(组合),而不用关心时序或底层的结果。 另外,一旦Promise决议,它就永远保持在这个状态。
Promise解决了因只用回调的代码而备受困扰的控制反转
问题。 但Promise也没有摈弃回调,只是把回调的安排转交给一位可信任的中介机制。
# # 定义
Promise是一个规范,意味着Promise实现的方式可以有很多种,但需要满足标准定义。以下是Promises/A+ 规范 (opens new window) (opens new window)中较为重要的部分。
Promise States
A promise must be in one of three states: pending, fulfilled, or rejected.
The then Method
A promise must provide a then method to access its current or eventual value or reason.
A promise’s then method accepts two arguments:
promise.then(onFulfilled, onRejected)
then must return a promise
2
3
4
5
6
7
8
9
10
# # 动手实现promise
以下是简化说明版,详细版本可以看我的github build-your-own-promise (opens new window) (opens new window)
function Promise(executor) {
var self = this
self.status = 'pending' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集
function resolve(value) {
if (self.status === 'pending') {
self.status = 'resolved'
self.data = value
for(var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
}
// 执行new Promise()时,传入的function。等于说每次新建new Promise实例,总会执行传入的函数
executor(resolve, reject)
}
Promise.prototype.then = function(onResolved, onRejected) {
var self = this // self = this = primise1
// 重要,根据Promise A+标准,then方法总是返回一个新的promise2 = new Promise(),这点非常重要
var promise2
// 状态处理,主流程大部分走pending
if (self.status === 'pending') {
return promise2 = new Promise(function(resolve, reject) {
// 包装的resolvedCallback。resolvedCallback调用时间:promise1调用resolve
var resolvedCallback = function(value) {
var x = onResolved(self.data)
resolve(x) // 重要。这是promise2的resolve,触发链式调用
}
// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,只能等到Promise的状态确定后,才能确实如何处理.
// 重要:第一个then()回调,放进promise1.callbacks,第二个then(),放进promise2.callback。
self.onResolvedCallback.push(resolvedCallback)
})
}
}
module.exports = Promise
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
new Promise(function(resolve, reject){
setTimeout(function(){
resolve('helloworld')
}, 0)
}).then(function(data) {
console.log(data, 1)
return 123
}).then(function(data) {
console.log(data, 2)
})
// 按顺序打印:
// helloworld 1
// 123 2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# # 解释说明
以上等价于:
var promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('helloworld')
}, 0)
})
var callback1 = function(data) {
console.log(data, 1)
return 123
}
var promise2 = promise1.then(callback1)
var callback2 = function(data) {
console.log(data, 2)
}
var promise3 = promise2.then(callback2)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
实例化new Promise(fn)
,意味着马上执行fn(),但fn()里一般是异步代码,返回数据时间是不一定的。接下来继续走同步代码then。
Promise.prototype.then
才是Promise实现的关键难点。根据Promise A+规范,then返回的是另外一个Promise(不能像jquery一样,返回this,每次都在同一个对象上操作)。因为Promise有三种状态:pending、fulfill、reject。而且默认pending状态改为fulfill/reject时,实例的promise就不能再改变状态了。
以上代码按照时间线执行路径:
线执行
fn()
,启动异步进程,同时同步代码then继续。执行promise1.then,
Promise.prototype.then
流程详细:- 会新创建new Promise(fn2),立马执行
fn2()
。fn2()中把包装callback1的函数resolvedCallback,放在promise1.callbacks中。啥时候promise1调用它的callbakcs呢?这个取决于用户,还记得new Promise(function(resolve, reject){})中的resolve函数吗?什么调用它(还能返回‘helloworld’数据),就会去执行这个resolvedCallback
。
- 会新创建new Promise(fn2),立马执行
var resolvedCallback = function(value) {
var x = onResolved(self.data) // onResolved就是callback1;self就是promise1,能拿到data是因为这个函数执行会在promise1.resolve(‘helloworld’)后
resolve(x) // 重要。这是promise2的resolve,触发链式调用
}
2
3
4
5
2. 返回一个新的promise2。
执行promise2.then,同上,执行
fn3()
。此时:(每个onResolvedCallback里的self是不一样的。)- promise1.callbacks = [包装的resolvedCallback]
- promise2.callbacks = [包装的resolvedCallback]
- promise3.callbacks= [包装的resolvedCallback]
异步代码拿到数据,用户手动触发第一个resolve
:promise1.resolve('helloworld'),星星之火点燃。- 此时,promise1的data变为‘helloworld’
- 执行promise1.callbacks[0], 拿到promise1.data,然后执行callback1(promise1.data),同时拿到返回值x。
- 触发promise2.resove(x),执行promise2.callbacks[0],让火继续烧下去。
以上只是简单叙述Promise实现原理,真实情况是可能有更多情形,比如在then中返回Promise等。