异步在JavaScript中最为常见,用最简单的回调函数callback
就解决问题。但是往往会陷入Callback Hell的困境之中。
Callback Hell
以最常见的AJAX GET为例:
1
2
3
4
5
6
7
8
| var makeAjaxGET = function(url, callback) {
var req = new XMLHttpRequest();
// do something
// callback result
};
makeAjaxGET('http://api.example.com', function(result) {
// handle result
});
|
这样看上去还好,但是如果想要继续嵌套请求呢?
1
2
3
4
5
6
7
| makeAjaxGET('http://api.example.com', function(result) {
makeAjaxGET('http://api.example.com' + result.id, function(result) {
makeAjaxGET('http://api.example.com' + result.newId, function(result) {
// handle result
});
});
});
|
光是那一排排的括号都看的人头晕了!
Promise
此时就需要Promise
出场解决问题了。
Promise是抽象异步处理对象以及对其进行各种操作的组件。
一个Promise
对象有三种状态:Pending
, Fullfilled
, Rejected
,对应未完成,成功,失败三种情况。
创建Promise
对象的方法很简单:
- 使用
new Promise(fn)
返回一个新的Promise
对象 - 在
fn
函数中处理异步结果:成功 -> 调用resolve(result)
;失败 -> 调用reject(reason / new Error)
成功获取Promise
对象后,用then
对结果进行处理即可 (Thenable)
Example
还是以AJAX GET为例:
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
| function ajaxGET(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(e) {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status < 400) {
resolve(xhr.response);
} else {
reject(new Error('Error: ' + xhr.statusText));
}
};
xhr.open('GET', url);
xhr.send(null);
});
}
ajaxGET('http://reqr.es/api/users?page=2')
.then(JSON.parse)
.then(function(result) {
console.log(result);
}).catch(function(error) {
// Handle Error
console.log(error);
});
|
经过Promise
封装后的AJAX调用过程清晰明了。此外利用Promise Chain
特性也可以做到共享AJAX的response
Future
Promise
规范已成为ECMAScript6
的一部分,和Generators
共同解决JavaScript异步编程的问题。最新版的Chrome(43)和Firefox(38)都可直接使用Promise
,不支持的浏览器还可使用各种Promise实现类库:Q
、bluebird
……
许多常用的类库都用到了Promise
技术,例如jQuery
,AngularJS
More: