Promise的日常使用
举个栗子:
1
2
3
4
5
6
7
8
9
10
11function getMsg() {
return new Promise(function(resolve) {
//异步请求
http.get(url,function(msg){
resolve(msg);
})
})
}
getMsg()
.then(res => console.log(res))
1 | function Promise(fn) { |
- 调用
then方法,将想要在Promise异步操作成功时执行的回调放入callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考; - 创建
Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用resolve方法,这时候其实真正执行的操作是将callbacks队列中的回调一一执行;
实现Promise延迟机制
细心的小明发现了一个问题,如果在
then方法注册回调之前,resolve函数就执行了,怎么办?比如promise内部的函数是同步函数:1
2
3
4
5
6function getMsg() {
return new Promise(function(resolve) {
//异步请求
resolve(123);
})
}Promises/A+规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。因此我们要加入一些处理,保证在resolve执行之前,then方法已经注册完所有的回调。这个时候就需要我们实现resolve方法的延时机制,使用setTimeout方法:1
2
3
4
5
6
7function resolve(value) {
setTimeout(function() {
callbacks.forEach(function (callback) {
callback(value);
});
}, 0)
}setTimeout会将callback的异步执行队列放在下一个macrotask里面,等执行了macrotask中的所有microtask后再执行下一个macrotask。具体可查询Javascript的event loop相关知识。完整Promise实现
1.Promise状态的引入
在上述代码中,我们大致实现了
Promise的基本功能,但小明通过敏锐的洞察力可观察到,即使在执行resolve会依次执行callback的所有注册方法,这样额外开销会非常大,这显然并不是我们想要的Promise,所以我们需要给Promise增加状态,当状态切换时,执行相应的回调方法。三种状态分别为pending、fulfilled、rejected,对应相应的回调方法。2.Promise状态的相互转换
Promises/A+规范Promise States中明确规定了,pending可以转化为fulfilled或rejected并且只能转化一次,也就是说如果pending转化到fulfilled状态,那么就不能再转化到rejected。也就是说在Promise的状态切换是不可逆的。并且fulfilled和rejected状态只能由pending转化而来,两者之间不能互相转换。可以看下图之前的切换:
代码的思路是这样的:
resolve执行时,会将状态设置为fulfilled,在此之后调用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
30function Promise(fn) {
var state = 'pending',
value = null,
callbacks = []; //callbacks为数组,因为可能同时有很多个回调
this.then = function (onFulfilled) {
if (state === 'pending') {
callbacks.push(onFulfilled);
//return this支持链式调用
return this;
}
//假如state已经切换至其他状态,直接执行回调
onFulfilled(value);
return this;
};
function resolve(newValue) {
state = 'fulfilled';
value = newValue;
setTimeout(function () {
callbacks.forEach(function (callback) {
callback(newValue);
});
}, 0);
}
fn(resolve);
}
3.链式Promise
但聪明的小明又发现了一个问题,当在
then里面执行一个回调时,假如在这里面返回一个Promise,类似于这样:1
2
3
4
5
6
7
8
9
10
11
12getMsg()
.then(getDetailMsg)
.then(function (detailMsg) {
// 对detailMsg的处理
});
function getDetailMsg(msg) {
return new Promise(function (resolve) {
http.get(url + msg, function(detailMsg) {
resolve(detailMsg);
});
});
}这就是传说中的链式
Promise循环,在第二个then中,我们希望它能挂钩到第一个Promise所resolve的参数。要达到我们想要的效果,需要修改then的源码,使其返回一个新的Promise.作为Promise实现最大的难题,这个我们后面继续实现。