Promise是异步编程的一种方案,ES6的一个新标准,目前大多数浏览器已经支持,而且还有一些第三方库也实现了该功能。Promise对象有两个特点,一是对象的状态不受外界影响,二是一旦状态改变,就不会再变,任何时候都可以得到这个结果。我们先来看一下Promise的简单用法…

Promise是异步编程的一种方案,ES6的一个新标准,目前大多数浏览器已经支持,而且还有一些第三方库也实现了该功能。Promise对象有两个特点,一是对象的状态不受外界影响,二是一旦状态改变,就不会再变,任何时候都可以得到这个结果。我们先来看一下Promise的简单用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var p = new Promise(function(resolve, reject) {  
...
// 事务触发
resovle(xxx);
...
reject(xxx);
});
p.then(function(value) {
// 满足
...
}, function(reason) {
// 拒绝
...
}).then().then()...

大致的情况是这样,Promise的构造函数接收一个函数作为参数,这个函数的参数是两个promise内部定义好的两个函数,resolve和reject。当满足一定的条件时,可以调用这两个函数中的一个,触发事务。Promise的状态会从pendding变为fulfilled或者rejected,然后调用.then()方法注册的回调函数,实现异步编程。.then()函数的返回值仍为Promise对象,因此可以进行链式调用,避免回调地狱的发生。大致流程图如下:
img

实现

首先我们整理出promise的三种状态机制,pendding(准备),fulfilled(即resolved,成功),rejected(失败)。然后定义一个数组hendlers来存放.then()里的回调函数。定义一个value来存储异步操作的数据(核心)。那么大致Promise看起来是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var PENDING = 0;  // 进行中  
var FULFILLED = 1; // 成功
var REJECTED = 2; // 失败
function Promise() {
// 存储PENDING, FULFILLED或者REJECTED的状态
var state = PENDING;
// 存储成功或失败的结果值
var value = null;
// 存储成功或失败的处理程序,通过调用`.then`或者`.done`方法
var handlers = [];
// 成功状态变化
function fulfill(result) {
state = FULFILLED;
value = result;
...
}
// 失败状态变化
function reject(error) {
state = REJECTED;
value = error;
}
}

然后我们来看Promise对象中的resolve方法,他的作用是代表异步操作成功,将数据’穿透’出去。

1
2
3
4
5
6
7
8
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

这里注意,这个value分两种情况,一种是普通值,那么就继续执行,如果是promise对象,则注意要等子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
function Promise() {  
...
function resolve(result) {
try {
// 如果是一个promise对象
if (result instanceof Promise) {
doResolve(result.then.bind(result), resolve, reject);
return;
}
// 修改状态,传递结果到下一个事务
fulfill(result);
} catch (e) {
reject(e);
}
}

// 继续执行promise处理
function doResolve(fn, onFulfilled, onRejected) {
var done = false;
try {
fn(function(value) {
if (done) return;
done = true;
onFulfilled(value);
}, function(reason) {
if (done) return;
done = true;
onRejected(reason);
});
} catch(ex) {
if (done) return;
done = true;
onRejected(ex);
}
}
}

这个fn就是构造promise时传递进来的函数,因此我们可以这样暴露出接口:

1
2
3
4
function Promise(fn) {  
...
doResolve(fn, resolve, reject);
}

目前,我们这一整套机制已经走到了流程图的前一半部分,触发事务,改变状态。接下来就是实现then()函数,我们先来看看怎么简单的实现,即不返回promise对象,一调用就结束整个流程的done(),结合之前所实现的代码:

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
var PENDING = 0;  // 进行中  
var FULFILLED = 1; // 成功
var REJECTED = 2; // 失败
function Promise() {
// 存储PENDING, FULFILLED或者REJECTED的状态
var state = PENDING;
// 存储成功或失败的结果值
var value = null;
// 存储成功或失败的处理程序,通过调用`.then`或者`.done`方法
var handlers = [];
// 成功状态变化
function fulfill(result) {
state = FULFILLED;
value = result;
handlers.forEach(handle);
handlers = null;
}
// 失败状态变化
function reject(error) {
state = REJECTED;
value = error;
handlers.forEach(handle);
handlers = null;
}
function resolve(result) {
try {
var then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject)
return
}
fulfill(result);
} catch (e) {
reject(e);
}
}
// 不同状态,进行不同的处理
function handle(handler) {
if (state === PENDING) {
handlers.push(handler);
} else {
if (state === FULFILLED &&
typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value);
}
if (state === REJECTED &&
typeof handler.onRejected === 'function') {
handler.onRejected(value);
}
}
}
this.done = function (onFulfilled, onRejected) {
// 保证异步
setTimeout(function () {
handle({
onFulfilled: onFulfilled,
onRejected: onRejected
});
}, 0);
}
doResolve(fn, resolve, reject);
}

我们结合promise的调用来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var p = new Promise(function(resolve, reject) {  
...
// 传入数据,改变state
resovle(xxx);
...
reject(xxx);
});
p.then(function(value) {
// 满足
...
}, function(reason) {
// 拒绝
...
}).then().then()...

初始化Promise以后就会触发resovle和reject函数并且把异步的参数传入,此时Promise内部发生的改变是:

  • 初始化Promise以后就会触发resovle和reject函数并且把异步的参数传入,此时Promise内部发生的改变是:
  • 判断此时handle中是否有需要等待执行的回调函数,若果有则将异步参数传入并且执行回调函数。

这里说明一下handlers的数据结构,它是一个数组,每个元素是一个对象,里面有两个属性,onFulfilled对应的值是成功是需要调用的回调函数,onReject对应的值是失败后需要调用的回调函数。

1
2
3
4
5
6
7
handlers = [{
onFulfilled : function xxx(){},
onRejected: function xxxx(){}
},{
onFulfilled : function xxx(){},
onRejected: function xxxx(){}
}]

那么这些回调函数是怎么穿进去的呢,很明显,就是在.then()函数里面,他接受两个参数,第一个onFulfilled类型,第二个是onRejected类型。我们已经实现了不返回promise的done方法,那么实现then就比较容易了,只要新建一个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
this.then = function (onFulfilled, onRejected) {  
var self = this;
return new Promise(function (resolve, reject) {
return self.done(function (result) {
if (typeof onFulfilled === 'function') {
try {
// onFulfilled方法要有返回值!
return resolve(onFulfilled(result));
} catch (ex) {
return reject(ex);
}
} else {
return resolve(result);
}
}, function (error) {
if (typeof onRejected === 'function') {
try {
return resolve(onRejected(error));
} catch (ex) {
return reject(ex);
}
} else {
return reject(error);
}
});
});
}

总结

其实大致的思路就是创建promise对象,使用then方法将回调函数先注入handlers,等到异步调用结束,执行resolve或者reject函数是改变state,触发handlers中之前注入的函数。最后再返回一个promise对象。通过本例我们可以看到js中函数是特等公民,可以作为参数,可以作为返回值,甚至是键值对。
完整代码