前言
古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在 JavaScript 中称为 Promise 对象。
Promise 最大的好处就是将异步操作从主逻辑中移除,异步回调处理放在 .then() or .catch()
中。
来看下一个经典的异步操作 AJAX
:
request.onreadystatechage = function () {
if (request.readyState === 4) {
// 执行成功回调
return success(request.responseText);
} else {
// 执行失败回调
return fail(request.status);
}
}
上面把回调函数 success(request.responseText)
和 fail(request.status)
写到一个 AJAX 里很正常,但是不好看,而且不利于代码复用。如果换成下面的写法呢:
var ajax = ajaxGet('xxx');
ajax.ifSuccess(success)
.ifFail(fail);
这种链式的写法好处在于,先统一执行 AJAX 逻辑,不关心如何处理结果。然后根据结果是成功还是失败,在将来的某个时刻调用 success
或 faild
。Promise 就是来解决这个问题的,请往下看!
Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
Promise 对象代表一个异步操作,有三种状态:pending
(进行中), fulfilled
(已成功), rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
状态变换只有这两种情况:
- pending -> fulfilled
- pending -> rejected
Promise 对象是一个构造函数,通过 new 关键字初始化得到新的 Promise 对象
const promise = new Promise(function(resolve, reject) {
// ... do something
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
})
Promise
实例生成以后,可以用 then
方法分别指定 resolved
状态和 catch
指定 rejected
状态的回调函数。这两个参数都接受 Promise
对象传出的值作为参数。
promise.then((result) => {
// success
}).catch((err) => {
// faild
})
Promise 新建后就会立即执行。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是 Promise
。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以 resolved
最后输出。
注意,调用 resolve
或 reject
并不会终结 Promise 的参数函数的执行。
new Promise((resolve, reject) => {
reslove(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
最好在它们前面加上 return
语句,这样就不会有意外。
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
下面是一个用 Promise
对象实现 Ajax 操作的例子:
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
Promise.all
Promise.all
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
这个 api 在开发中也比较实用,如果 C 行为依赖于 A、B 行为,A、B 之间又没有依赖关系,而且只有当 A、B 的状态都为 fulfilled,或者有一个变为 rejected 时才会开始 C 行为。这时用 Promise.all() 就显得比较合适。
var p1 = new Promise((reslove, reject) => {
setTimeout(reslove, 5000, 'PI');
})
var p2 = new Promise((reslove, reject) => {
setTimeout(reslove, 600, 'P2');
})
Promise.all([p1, p2]).then(([p1, p2]) => {
console.log(p1);
console.log(p2);
})
Promise.race
Promise.race
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
用法和 Promise.all 一样,C 依赖于 A、B,但只要 A,B 的任一状态改变了,C 便开始执行。
var p1 = new Promise((reslove, reject) => {
setTimeout(reslove, 5000, 'PI');
})
var p2 = new Promise((reslove, reject) => {
setTimeout(reslove, 600, 'P2');
})
Promise.race([p1, p2]).then((result) => {
console.log(result);
})
网友评论