你的Promise

你的Promise

在JavaScript的世界里,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。
js中异步编程主要指的是setTimout/setInterval、DOM事件机制、ajax,通过传入回调函数实现控制反转。异步编程为js带来强大灵活性的同时,也带来了嵌套回调的问题。

1
2
3
4
5
6
function callback() {
console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');

串行执行若干异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function ajax(method,url,data){
var request=new XMLHttpRequest();
return new Promise(function(resolve,reject){
if(request.readyState===4){
if(request.status===200){
resolve(request.responseText);
}else{
reject(request.status);
}
}
request.open(method,url);
request.send(data);
});
}
var log=document.getElementById("div");
var p=ajax('GET','/api/categories');
p.then(function(text){
log.innerText=text;
}).catch(function(status){
log.innerText='ERROR:'+status;
});

Promise还可以并行执行异步任务

  1. Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例
    var p=Promise.all([p1,p2,p3]);p1,p2,p3都是Promise对象的实例(Promise.all()参数只要是Iterator接口)分两种状态:
  • p1,p2,p3三者都是Fulfilled,p的状态才变成Fulfilled
  • 只要p1,p2,p3中有一个被rejected则p就被rejected

试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:

1
2
3
4
5
6
7
8
9
10
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});

有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

  1. Promise.race()方法,
    var Promise.race([p1,p2,p3]);只要p1,p2,p3有一个实例率先改变状态,p的状态就会跟着改变,并且返回改变的那个实例的返回值就给p的回调函数。(而那些后面的返回值仍在执行,但被丢弃)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
    });
    var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
    });
    Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
    });

Promise的附加方法

  1. done()
    Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。
    1
    2
    3
    4
    5
    asyncFunc()
    .then(f1)
    .catch(r1)
    .then(f2)
    .done();

它的实现代码非常简单

1
2
3
4
5
6
7
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// 抛出一个全局错误
setTimeout(() => { throw reason }, 0);
});
};

  1. finally()
    finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
    下面是一个例子,服务器使用Promise处理请求,然后使用finally方法关掉服务器。
    1
    2
    3
    4
    5
    server.listen(0)
    .then(function () {
    // run test
    })
    .finally(server.stop);

它的实现也很简单。

1
2
3
4
5
6
7
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};

参考链接:
http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000

文章目录
  1. 1. 你的Promise
    1. 1.1. 串行执行若干异步
    2. 1.2. Promise还可以并行执行异步任务
    3. 1.3. Promise的附加方法
|