概述
Promise 主要是为解决程序异步处理而生的,Promise 接受异步任务并立即执行,然后在任务完成后,将状态标注成最终结果(成功或失败)。
Promise 有三种状态:初始化时,刚开始执行主体任务,这时它的初始状态时 pending(进行中)**;**等到任务执行完成,这时根据成功或失败,分别对应状态 fulfilled(成功)和 rejected(失败),这时的状态就固定不能被改变了,即 Promise 状态是不可逆的
基本用法
Promise 就是一个类,所以使用时,我们照常 new 一个实例即可。
const myPromise = new Promise((resolve, reject) =>
// 这里是 Promise 主体,执行异步任务
ajax('xxx', () => {
resolve('成功了'); // 或 reject('失败了')
})
})
上面创建好 Promise 实例后,里面的主体会立即执行,比如,如果是发送请求,则会立即把请求发出去,如果是定时器,则会立即启动计时。至于请求什么时候返回,我们就在返回成功的地方,通过 resolve() 将状态标注为成功即可,同时 resolve(data) 可以附带着返回数据。
然后在 then() 里面进行回调处理。
const myPromise = new Promise((resolve, reject) => {
// 这里是 Promise 主体,执行异步任务
ajax('xxx', () => {
resolve('成功了');
})
})
myPromise.then((data) => {
// 处理 data 数据
});
当初始化 Promise 实例时,主体代码是同步就开始执行了的,只有 then() 里面的回调处理才是异步的,因为它需要等待主体任务执行结束。
const myPromise = new Promise((resolve, reject) => {
// 这里是 Promise 主体,执行异步任务
console.log(1);
ajax('xxx', () => {
resolve('成功了');
})
}).then(() => {
console.log(2);
})
console.log(3);// 最终输出 1、3、2
Promise异常处理
- 方式一,通过 then() 的第 2 个参数
const myPromise = new Promise(...);
myPromise.then(successCallback, errorCallback);
这种方式能捕获到 promise 主体里面的异常,并执行 errorCallback。但是如果 Promise 主体里面没有异常,然后进入到 successCallback 里面发生了异常,此时将不会进入到 errorCallback。因此我们经常使用下面的方式二来处理异常。
- 方式二,通过 catch() (常用方案)
const myPromise = new Promise(...);
myPromise.then(successCallback).catch(errorCallback);
- 方式三,try…catch
try catch 是传统的异常捕获方式,这里只能捕获同步代码的异常,并不能捕获异步异常,因此无法对 Promise 进行完整的异常捕获。
链式调用
Promise 的链式调用,每次调用后,会返回一个新的 Promise 实例对象,从而可以继续 then()或者其他 API 调用
const myPromise = new Promise((resolve) => {
resolve(1)
}).then((data) => {
return data + 1;
})).then((data) => {
console.log(data)
};
// 输出 2
每次 then() 或者 catch() 后,返回的是一个新的 Promise,和上一次的 Promise 实例对象已经不是同一个引用了。而这个新的 Promise 实例对象包含了上一次 then 里面的结果,这也是为什么链式调用的 catch 才能捕获到上一次 then 里面的异常的原因
下面就是非链式调用
const myPromise = new Promise((resolve) => {
resolve(1)
})
myPromise.then((data) => {
return data + 1;
})
myPromise.then((data) => {
console.log(data);
})
//
// 输出 1
常用API
Promise API 和大部分类一样,分为实例 API 或原型方法(即 new 出来的对象上的方法),和静态 API 或类方法(即直接通过类名调用,不需要 new)。注意实例 API 都是可以通过链式调用的。
实例 API(原型方法)
- then():Promise 主体任务和在此之前的链式调用里的回调任务都成功的时候(即前面通过 resolve 标注状态后),进入本次 then() 回调。
- catch():Promise 主体任务和在此之前的链式调用里的出现了异常,并且在此之前未被捕获的时候(即前面通过 reject 标注状态或者出现 JS 原生报错没处理的时候),进入本次 catch()回调。
- finally():无论前面出现成功还是失败,最终都会执行这个方法(如果添加过)。比如某个任务无论成功还是失败,我们都希望能告诉用户任务已经执行结束了,就可以使用 finally()
静态 API(类方法)
- Promise.resolve():返回一个成功状态的 Promise 实例,一般常用于构建微任务,比如有个耗时操作,我们不希望阻塞主程序,就把它放到微任务去,如下输出 1、3、2,即 console.log(2) 将放到最后微任务去执行
console.log(1);
Promise.resolve().then(() => {
console.log(2); // 作为微任务输出 2
})
console.log(3);
- Promise.reject():这个与 Promise.resolve 使用类似,返回一个失败状态的 Promise 实例。
- Promise.all():此方法接收一个数组为参数(准确说是可迭代参数),数组里面每一项都是一个单独的 Promise 实例,此方法返回一个 Promise 对象。这个返回的对象含义是数组中所有 Promise 都返回了(可失败可成功),返回 Promise 对象就算完成了。适用于需要并发执行任务时,比如同时发送多个请求。
const p1 = new Promise(...);
const p2 = new Promise(...);
const p3 = new Promise(...);
const pAll = Promise.all([p1, p2, p3]);
pAll.then((list) => {
// p1,p2,p3 都成功了即都 resolve 了,会进入这里;
// list 按顺序为 p1,p2,p3 的 resolve 携带的返回值
}).catch(() => {
// p1,p2,p3 有至少一个失败,其他成功,就会进入这里;
})
注意 Promise.all 是所有传入的值都返回状态了,才会最终进入 then 或 catch 回调。
Promise 的参数也可以如下常量,它会转换成立即完成的 Promise 对象:
Promise.all([1, 2, 3]);
// 等同于
const p1 = new Promise(resolve => resolve(1));
const p2 = new Promise(resolve => resolve(2));
const p3 = new Promise(resolve => resolve(3));
Promise.all([p1, p2, p3]);
- Promise.race():与 Promise.all() 类似,不过区别是 Promise.race 只要传入的 Promise 对象,有一个状态变化了,就会立即结束,而不会等待其他 Promise 对象返回。