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实现最大的难题,这个我们后面继续实现。