1.概述
在JavaScript中,所有的代码都是单线程执行的,由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口。Promise起到代理(proxy)的作用,使得异步操作具备同步操作的接口,Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。
异步执行可以调用回调函数实现:
function callback() {
console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');
//控制台输出结果:
before setTimeout()
after setTimeout()
(等待1秒后)
Done
简单地说,异步操作会在将来的某个事件点触发一个函数调用。
ajax是典型的异步操作。以上一节的代码为例:
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.responseText);
} else {
return fail(request.status);
}
}
}
首先,Promise 是一个对象,也是一个构造函数。
function f1(resolve, reject) {
// 异步代码...
}
var p1 = new Promise(f1);
Promise构造函数接受一个回调函数f1作为参数,f1里面是异步操作的代码。然后,返回的p1就是一个 Promise 实例
Promise 的设计思想是,所有异步任务都返回一个 Promise 实例。Promise 实例有一个then方法,用来指定下一步的回调函数。
// Promise
function f1(){
...
}
var p1 = new Promise(f1);
p1.then(f2);
传统的写法可能需要把f2作为回调函数传入f1,比如写成f1(f2),异步操作完成后,在f1内部调用f2。Promise 使得f1和f2变成了链式写法。不仅改善了可读性,而且对于多层嵌套的回调函数尤其方便。
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// ...
});
});
});
});
// Promise 的写法
(new Promise(step1))
.then(step2)
.then(step3)
.then(step4);
2.Promise 对象的状态
Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。
- 异步操作未完成(pending)
- 异步操作成功(fulfilled)
- 异步操作失败(rejected)
状态变化:
- 未完成 to 成功
- 未完成 to 失败
Promise的最终结果:
- 异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
- 异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。
3.Promise 构造函数
var promise = new Promise(function (resolve, reject) {
// ...
if (/* 异步操作成功 */){
resolve(value);
} else { /* 异步操作失败 */
reject(new Error());
}
});
4. .then()
var p1 = new Promise(function (resolve, reject) {
resolve('成功');
});
p1.then(console.log, console.error);
// "成功"
var p2 = new Promise(function (resolve, reject) {
reject(new Error('失败'));
});
p2.then(console.log, console.error);
// Error: 失败
then()的用法:
f1().then(function () {
return f2();
}).then(f3);
//f3回调函数的参数,是f2函数的运行结果。
f1().then(function () {
f2();
return;
}).then(f3);
//f3回调函数的参数是undefined
f1().then(f2())
.then(f3);
//f3回调函数的参数,是f2函数返回的函数的运行结果
f1().then(f2)
.then(f3);
//f2会接收到f1()返回的结果,f3回调函数的参数,是f2函数的运行结果。
5. Promise的优势
Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了:
promise.png
Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:
job1.then(job2).then(job3).catch(handleError);
除了串行执行若干异步任务外,Promise还可以并行执行异步任务。
例如一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的用 Promise.all()实现
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()实现:
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'
});
//由于p1执行较快,Promise的then()将获得结果'P1'。p2仍在继续执行,但执行结果将被丢弃。
传送门
网友评论