共计 5537 个字符,预计需要花费 14 分钟才能阅读完成。
Promise 含义
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。
所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise 对象有以下两个特点:
对象的状态不受外界影响。有三种状态,分别为 pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态改变只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。
使用 Promise 对象的好处在于:
可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise 缺点:
无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
基本用法
ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.random();
if (num > 0.5) {
resolve(num);
} else {
reject(num);
}
}, 500);
});
promise.then(
res => {
console.log(“ 成功:” + res);
},
err => {
console.log(“ 失败:” + err);
}
);
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve 函数的作用:将 Promise 对象的状态从“未完成(pending)”变为“成功(resolved)”,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
reject 函数的作用:将 Promise 对象的状态从“未完成(pending)”变为“失败(rejected)”在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
then 方法作用:接受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。第二个函数可选,不一定要提供,也可以将第二个函数作为 catch 方法的参数。
catch 方法作用:用于指定发生错误时的回调函数。Promise 对象异步操作抛出错误,状态就会变为 rejected,就会调用 catch 方法指定的回调函数处理这个错误。另外,then 方法指定的回调函数,如果运行中抛出错误,也会被 catch 方法捕获。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.random();
if (num > 0.5) {
resolve(num);
} else {
reject(num);
}
}, 500);
});
promise
.then(res => {
console.log(“ 成功:” + res);
})
.catch(err => {
console.log(“ 失败:” + err);
});
promise
.then(res => {
console.log(“ 成功:” + res);
throw new Error(“test”);
})
.catch(err => {
// num > 0.5 时打印 “ 失败:Error: test”
console.log(“ 失败:” + err);
});
Promise 执行顺序
Promise 新建后立即执行,then 方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,catch 同理。
调用 resolve 或 reject 并不会终结 Promise 的参数函数的执行。
const promise = new Promise((resolve, reject) => {
console.log(“ 我是第一个执行的 ”);
resolve();
});
promise.then(res => {
console.log(“ 我是第三个执行的 ”);
});
console.log(“ 我是第二个执行的 ”);
resolve 函数和 reject 函数的参数
reject 函数的参数通常是 Error 对象的实例,表示抛出的错误;resolve 函数的参数除了正常的值以外,还可能是另一个 Promise 实例。
如果一个 Promise(P2)的 resolve 参数是另一个 Promise(P1),此时 P1 的状态就会传给 P2,P1 的状态决定了 P2 的状态,P1 的状态改变,P2 的回调函数才会执行。
const p1 = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error(“fail”)), 3000);
});
const p2 = new Promise(function(resolve, reject) {
setTimeout(() => resolve(p1), 1000);
});
p2.then(result => console.log(result)).catch(error => console.log(error));
// Error: fail
上面代码中,p1 是一个 Promise,3 秒之后变为 rejected。p2 的状态在 1 秒之后改变,resolve 方法返回的是 p1。由于 p2 返回的是另一个 Promise,导致 p2 自己的状态无效了,由 p1 的状态决定 p2 的状态。所以,后面的 then 语句都变成针对后者(p1)。又过了 2 秒,p1 变为 rejected,导致触发 catch 方法指定的回调函数。
Promise 链式调用
then 方法可以返回一个新的 Promise 实例(注意,不是原来那个 Promise 实例)。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。
const promise = new Promise((resolve, reject) => {
resolve(“promise”);
})
.then(res => {
console.log(res); // promise
return “promise1”;
})
.then(res => {
console.log(res); // promise1
return “promise2”;
})
.then(res => {
console.log(res); // promise2
});
注意:只要一个 Promise 中抛出错误,将执行 catch 方法,then 链终止。
const promise = new Promise((resolve, reject) => {
resolve(“promise”);
})
.then(res => {
console.log(res); // promise
throw new Error(“ 中止 ”);
return “promise1”;
})
.then(res => {
console.log(res);
return “promise2”;
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err); // Error: 中止
});
主动终止 then 链,通过 catch 方法来中止 promise chain
const promise = new Promise((resolve, reject) => {
resolve(“promise”);
})
.then(res => {
console.log(res); // promise
return Promise.reject({
notRealPromiseException: true
});
})
.then(res => {
console.log(res);
return “promise2”;
})
.then(res => {
console.log(res);
})
.catch(err => {
if (err.notRealPromiseException) {
return true;
}
console.log(err);
});
Promise.prototype.finally()
finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
finally 本质上是 then 方法的特例,不接受任何参数,不依赖于 Promise 的执行结果
promise.finally(() => {
// 语句
});
// 等同于
promise.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
Promise.all()
Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const promise = Promise.all([promise1, promise2, promise3])
Promise.all 方法接受一个数组作为参数,promise、pro 米色、promise3 都是 Promise 实例,如果不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。(Promise.all 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。了解 Iterator 接口)
promise 的状态由 promise1、promise2、promise3 决定,分成两种情况。
只有 promise1、promise2、promise3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 promise1、promise2、promise3 的返回值组成一个数组,传递给 p 的回调函数。
只要 promise1、promise2、promise3 之中有一个被 rejected,promise 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 promise 的回调函数。
const p1 = new Promise((resolve, reject) => {
resolve(“hello”);
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error(“ 报错了 ”);
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// [“hello”, Error: 报错了]
上面代码中,p1 会 resolved,p2 首先会 rejected,但是 p2 有自己的 catch 方法,该方法返回的是一个新的 Promise 实例,p2 指向的实际上是这个实例。该实例执行完 catch 方法后,也会变成 resolved,导致 Promise.all() 方法参数里面的两个实例都会 resolved,因此会调用 then 方法指定的回调函数,而不会调用 catch 方法指定的回调函数。
如果 p2 没有自己的 catch 方法,就会调用 Promise.all() 的 catch 方法。
Promise.race()
Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3])
只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。
Promise.race 方法的参数与 Promise.all 方法一样,如果不是 Promise 实例,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。
Promise.resolve()
将现有对象转化为 Promise 对象。
const promise = Promise.resolve(‘Hello world’)
参数是 Promise 实例,该方法不做任何改变。
参数是一个 thenable 对象,先将对象转为 Promise 对象,然后立即执行 thenable 方法。相当于将 thenable 对象中的 then 方法处理的值作为参数传给 promise then 方法。
let thenObj = {
then(resolve, reject) {
resolve(“Hello”);
}
};
const promise = Promise.resolve(thenObj);
promise.then(res => {
console.log(res); // Hello
});
参数不是具有 then 方法的对象,或根本就不是对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。
Promise.reject()
Promise.reject(reason) 方法也会返回一个新的 Promise 实例,该实例的状态为 rejected。