1 Promise 是什么

Promise 可以被理解为对象(或值)的代理,被代理的对象在被创建时状态是未知的,当被代理对象执行完成时,可以通过设置 Promise 的状态来触发相应的回调函数。

Promise 有下面三种状态:

  • pending:初始状态,既不是成功,也不是失败
  • fulfiled:操作成功
  • rejected:操作失败

下面例子中,通过 Promise 的构造函数参数来配置异步操作,通过 then 的回调来配置成功、失败回调函数。当异步操作完成后,根据其执行结果,通过调用 resolve 或 reject 来设置 Promise 的对应状态,进而触发相应的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
new Promise( function(resolve, reject) {
// 做一些异步操作,根据执行结果调用下面两者之一:
//
// resolve(someValue); //执行成功时,调用 resolve 来触发 fulfilled 状态
// ?或
// reject("failure reason"); //执行失败时,调用 reject 来触发 rejected 状态
// 当操作抛出异常时,会触发 promise 的 reject 状态

}).then(function resolveCall(result){
//当构造函数中调用 resolve 时,promise 状态变为 fulfiled, 进而触发 resolveCall 回调
}), function rejectCall(result){
//当构造函数中调用 reject 时,promise 状态变为 rejected,进而会触发 rejectCall 回调
});

2 Promise 的作用

Promise 可以将异步操作和其回调函数按照同步的方式进行书写,从而解决多层异步调用产生的回调地狱问题。

3 Promise 使用方法

下面所有方法的返回值都是新的 Promise 对象

3.1 创建 Promise

1
new Promise(function(resolve, reject){...} /*executor*/)

参数

​ executor

executor 回调有两个参数 resolvereject ,这两个参数是函数类型。创建 Promiseexecutor会被立即执行。当 resolvereject 函数被调用时,表示需要将 Promise 对象的状态设置为 fullfilled 或 rejected,并触发对应状态的回调函数的执行。

1
2
3
4
5
6
7
const myFirstPromise = new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
// resolve(someValue) // fulfilled
// or
// reject("failure reason") // rejected
});

3.2 类方法

Promise.all(iterable)

参数:interable 是多个 Promsie 对象的集合

作用:用于并行运行多个 Promsie 对象。

特点:

​ 一旦有一个子 Promsie 参数失败,会触发父 Promsie 对象的失败状态,并将失败信息传递给父 Promsie 的失败回调函数。

​ 当所有子 Promsie 参数都成功时,会触发父 Promsie 对象的成功状态,并将所有子 Promsie 参数的返回值按照顺序,以数组的形式传递给父 promise 的成功回调函数。

Promise.allSettled(iterable)

参数:interable 是多个 Promsie 对象的集合

作用:用于并行运行多个 Promsie 对象。

特点:

​ 当所有子 Promsie 参数都完成(无论成功还是失败)时,会触发父 Promsie 对象完成状态,并将所有子 Promsie 参数的返回值按照顺序,以数组的形式返回。

1
2
3
4
5
6
7
8
9
10
11
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));

// expected output:
// "fulfilled"
// "rejected"

Promise.race(iterable)

参数:interable 是多个 Promsie 对象的集合

作用:用于并行运行 多个 Promsie 对象。

特点:

父 Promsie 的状态会和最快产生状态的子 Promsie 保持一致,并将最快子 Promsie 的返回值作为父 Promsie 相应状态回调的参数。

Promise.reject(reason)

作用:创建一个状态为失败的 Promise 对象,并将 reason 参数传递给对应的处理方法

Promise.resolve(value)

作用:创建一个状态由给定 value 决定的 Promise 对象。

特点:

如果 value 是 thenable (即,带有then方法的对象),返回的 Promise 对象的最终状态由then方法执行决定;否则的话(value 为空、基本类型或者不带 then 方法的对象),返回的 Promise 对象状态为 fulfilled,并且将该value 传递给对应的 then 方法。

通常而言,如果你不知道一个值是否是 Promise 对象,使用 Promise.resolve(value) 来返回一个 Promise 对象,这样就能将 value 作为 Promise 对象来使用。

3.3 实例方法

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
var promise = new Promise( function(resolve, reject) {
//异步操作
});

promise.then(function(){

},function(){

}.catch(function(){

}).finally(function(){

});

Promise.prototype.then(onFulfilled, onRejected)

给 Promise 对象添加 fulfiled 和 rejected 状态的回调。

Promise.prototype.catch(onRejected)

给 Promise 对象添加 rejected 状态的回调。

Promise.prototype.finally(onFinally)

给 Promise 对象添加一个一定会执行的回调。无论当前 Promise 的状态是完成(fulfilled)还是失败(rejected)

4 注意

4.1 回调执行时序

1、Promise 被创建时,配置给 Promise 构造函数的回调会被立即执行。

2、当多次添加 then 回调时,这些回调会按照被插入顺序独立执行。 配置给 Promise.then 的回调会被放入微队列,本轮事件循环运行完成之前这些回调都不会被执行。

4.2 链式调用

Promise 的链式调用,可以解决按顺序执行多个异步调用的诉求。

1
2
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

promise2 代表 doSomething 、successCallback/failureCallback 已完成。

任何 promise2 新增的回调,都会在doSomething 、successCallback/failureCallback 之后执行。如果 successCallback/failureCallback 返回了新的 Promise 对象,则 promise2 会在 新 Promise 对象之后执行。

每一个 promise 的返回值,都是下一个promsie 回调函数的参数。

1
2
3
4
5
6
7
8
9
new Promise(function(resolve, reject) {
resolve(1);
})
.then(function(result) {
console.log(result);// 1
})
.catch(function(result) {
console.log(result);
});

4.3 组合

Promise.all()Promise.race() 是并行运行异步操作的两个组合式工具。

1
2
Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

也可以使用 JavaScript 写法实现时序组合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func1 = function(lastValue) {
return lastValue + " fun1";
};

func2 = function(lastValue) {
return lastValue + " fun2";
};

func3 = function(lastValue) {
return lastValue + " fun3";
};

[func1, func2, func3]
.reduce((totalValue, curArrItem) => {
return totalValue.then(curArrItem);
}, Promise.resolve("start"))
.then(result3 => {
console.log("results:", result3);
});

//上面代码实现,相当于一个 Promise 链,如:
Promise.resolve().then(func1).then(func2).then(func3);

4.4 嵌套

最好不好嵌套 Promise,容易出错。

1
2
3
4
5
6
7
8
9
10
doSomethingCritical()
.then(result =>
doSomethingOptional()
.then(optionalResult => doSomethingExtraNice(optionalResult))
.catch(e => {
console.log(e.message);
})
) // 即使有异常也会忽略,继续运行;(最后会输出)
.then(() => moreCriticalStuff())
.catch(e => console.log("Critical failure: " + e.message)); // 没有输出

这个内部的 catch 语句仅能捕获到 doSomethingOptional()doSomethingExtraNice() 的失败,之后会继续moreCriticalStuff() 的运行。

4.5 异常

当有异常时,Promise 会中断,然后执行离的最近的 catch 或 rejectCallBack 回调来处理异常,当异常处理完成后,如果后面还有 then,则继续执行后面的 then 回调。

4.6 Promise 拒绝事件

当 Promise 触发 rejected 状态时,会有下面事件之一被派发到全局作用域(通常而言,就是window;如果是在 web worker 中使用的话,就是 Worker 或者其他 worker-based 接口):

rejectionhandled

当 Promise 被拒绝、并且在 reject 函数处理该 rejection 之后会派发此事件

unhandledrejection

当 Promise 被拒绝,但没有提供 reject 函数来处理该 rejection 时,会派发此事件。

1
2
3
4
5
6
7
window.addEventListener("unhandledrejection", event => {
/* 你可以在这里添加一些代码,以便检查
event.promise 中的 promise 和
event.reason 中的 rejection 原因 */

event.preventDefault();//告诉 JavaScript 引擎当 promise 被拒绝时不要执行默认操作,默认操作一般会包含把错误打印到控制台
}, false);