Promise对象
原文地址:https://wangdoc.com/javascript/
概述
Promise对象是JavaScript的异步操作解决方案,为异步操作提供统一接口。它起到代理作用,充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise可以让异步操作写起来,就像在写同步操作的流程,而不必一层层嵌套回调函数。
首先,Promise是一个对象,也是一个构造函数。
function f1(resolve, reject) {
// 异步代码
}
var p1 = new Promise(f1);
上面代码中,Promise构造函数接受一个回调函数f1作为参数,f1里面是异步操作的代码。然后,返回的p1就是一个Promise实例。
Promise的设计思想是,所有异步任务都返回一个Promise实例。Promise实例有一个then方法,用来指定下一步的回调函数。
var p1 = new Promise(f1);
p1.then(f2);
上面代码中,f1的异步操作完成,就会执行f2。
传统的写法可能需要把f2作为回调函数传入f1,比如写成f1(f2),异步操作完成后,在f1内部调用f2。Promise使得f1和f2变成了链式写法。不仅改善了可读性,而且对于多层嵌套的回调函数尤其方便。
Promise对象的状态
Promise对象通过自身的状态,来控制异步操作。Promise实例具有三种状态。
- 异步操作未完成(pending)
- 异步操作成功(fulfilled)
- 异步操作失败(rejected)
上面三种状态里面,fulfilled和rejected合在一起称为resolved(已定型)。
Promise的最终结果只有两种。 - 异步操作成功,Promise实例传回一个值(value),状态变为fulfilled。
- 异步操作失败,Promise实例抛出一个错误(error),状态变为rejected。
Promise构造函数
var promise = new Promise(function (resolve, reject) {
if (/* 异步操作成功 */) {
resolve(value);
} else { /* 异步操作失败 */
reject(new Error());
}
});
上面代码中,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己实现。
resolve函数作用是,将Promise实例状态从pending变为fulfilled,在异步调用成功时调用,并将异步调用结果作为参数传递出去。reject函数的作用是,将Promise实例的状态从pending变为rejected,在异步操作失败时调用,并将异步操作报错,作为参数传递出去。
下面是一个例子:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, "done"); // "done"作为参数传入resolve()
});
}
timeout(100);
上面代码中,timeout(100)返回一个Promise实例。100毫秒以后,该实例的状态会变为fulfilled。
Promise.prototype.then()
Promise实例的then方法,用来添加回调函数。
then方法接受两个回调函数,第一个是异步操作成功时(变为fulfilled状态的回调函数),第二个是异步操作失败(变为rejected)时的回调函数(该参数可以省略)。一旦状态改变,就调用相应的回调函数。
var p1 = new Promise(function (resolve, reject) {
resolve("success");
});
p1.then(console.log, console.error);
var p2 = new Promise(function (resolve, reject) {
reject(new Error("failed"));
});
p2.then(console.log, console.error);
上面代码中,p1和p2都是Promise实例,它们then方法绑定两个回调函数:成功时回调函数console.log,失败时的回调函数console.error(可以省略)。
then方法可以链式调用。
p1.then(step1)
.then(step2)
.then(step2)
.then(console.log, console.error);
上面代码中,p1后面有四个then,意味着依次有四个回调函数。只要前一步的状态变为fulfilled,就会依次执行紧跟着在后面的回调函数。
最后一个then方法,回调函数是console.log和console.error,用法上有一个重要的区别。console.log只显示step3的返回值,而console.error可以显示p1、step1、step2、step3之中任意一个发生的错误。
then()用法辨析
Promise的用法,简单说就是一句话:使用then方法添加回调函数。
写法一
f1().then(function () {
return f2();
}).then(f3);
上面代码,f3的参数是f2函数运行的结果。
写法二
f1().then(function () {
f2();
}).then(f3);
上面代码,f3的参数是undefined。
写法三
f1().then(f2()).then(f3);
上面代码,f3的参数是f2函数返回的函数运行的结果。
写法四
f1().then(f2).then(f3);
写法四与写法一只有一个差别,那就是f2会接收到f1()返回的结果。
实例:图片加载
下面是使用Promise完成图片的加载。
var preloadImage = function (path) {
return new Promise(function (resolve, reject) {
var image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
preloadImage("url").then(function (e) {
document.body.append(e.target);
}).then(function () {
console.log('加载成功');
});