你的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还可以并行执行异步任务
- 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()实现:
- 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的附加方法
- 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); }); };
|
- 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