网站建设板块建议,godaddy 搭建网站,开店做网站有什么好处,二手书屋网站开发的意义Promise 类的方法简介
Promise 的 API 分为两种#xff1a; Promise 实例的方法#xff08;也称为#xff1a;Promis的实例方法#xff09; Promise 类的方法#xff08;也称为#xff1a;Promise的静态方法#xff09;
前面几篇文章#xff0c;讲的都是 Promise 实…Promise 类的方法简介
Promise 的 API 分为两种 Promise 实例的方法也称为Promis的实例方法 Promise 类的方法也称为Promise的静态方法
前面几篇文章讲的都是 Promise 实例的方法需要先将Promise实例化它们都是存放在Promise的prototype上的。今天这篇文章我们来讲一下 Promise 类的方法。
Promise 类的方法可以直接通过大写的Promise.xxx调用的方法。这里的xxx就称之为静态方法。
Promise 的自带 API 提供了如下静态方法
Promise 的静态方法含义版本Promise.resolve()返回一个成功状态的 Promise 对象ES 2015Promise.reject()返回一个失败状态的 Promise 对象ES 2015Promsie.all()所有 Promise 都执行成功才算成功或者任意一个 Promise 执行失败就算失败ES 2015Proimse.allSettled()不论成功与失败把所有Promise的执行结果全部返回ES 2020Promise.race()Promise集合中返回第一个执行完成无论成功与失败的 PromiseES 2015Promise.any()Promise集合中返回第一个执行成功的PromiseES 2021 Promise.resolve() 和 Promise.reject()
使用场景
当我们在定义一个 Promise 的过程中如果涉及到异步操作那就需要通过new Promise的方式创建一个 Promise 实例。
但有些场景下我们已经有一个现成的内容了希望将其转成 Promise 来使用。此时我们可以用 Promise.resolve() 将其封装为成功的状态。同理用Promise.reject()可以封装为失败的状态。
比如说有时候promise 里面并没有异步操作我只是单纯地想通过 promise 的方式返回一个字符串有的业务就是有这样的需求那就可以通过 Promise.reslove(字符串)、 Promise.reject(字符串) 这种简写的方式返回。
代码举例 const promise Promise.resolve(qianguyihao)promise.then(res {console.log(res:, res);});// 上方代码如果是连续书写的话也可以简写成Promise.resolve(qianguyihao).then(res console.log(res:, res));
Promise.resolve(qianguyihao) 这种写法似乎过于啰嗦直接 return qianguyihao不行吗that depands。举个例子我们在调用别人的方法时对方如果要求返回值必须是 Promise对象那么Promise.resolve() 就能派上用场了。
Promise.resolve()和Promise.reject()的返回值就是一个 Promise。
用法拆解
Promise.resolve()的用法相当于new Promise()并执行resolve()操作。下面这两种写法是等价的 // 写法1Promise 类的 resolve() 方法const promise Promise.resolve(params);// 写法2Promise 实例的 resolve() 方法const promise new Promise((resolve, reject) resolve(params));
Promise.reject()的用法同理。下面这两种写法是等价的 // 写法1Promise 类的 reject() 方法const promise Promise.reject(params);// 写法2Promise 实例的 reject() 方法// 第一个形参用不到我们通常用 下划线 表示。这是一种约定俗成的规范写法。const promise new Promise((_, reject) reject(params));
写法2显然过于啰嗦写法1用得更多。
写法2中我们可以学到一个写代码的小技巧如果某个形参我们用不到但又必须写出来的话我们通常用下划线表示。这是一种约定俗成的规范写法比较简洁。
resolve()和reject()的参数
resolve()参数中传入的值可以有很多种类型进而决定 Promise 的状态 情况1如果resolve()中传入普通的值或者普通对象那么这个值会作为then()回调的参数。Promise 的状态为fulfilled。 情况2如果resolve()中传入的是另外一个新的 Promise那么原 Promise 的状态将交给新的 Promise 决定。 情况3如果resolve()中传入的是thenable 对象那就会执行该then()方法并且根据then()方法的结果来决定Promise的状态。
reject()的参数中无论传入什么值Promise都会直接进入 rejected 状态并触发 catch() 方法的执行。
我们在前面的文章《Promise入门详解》中针对这些情况做了详细介绍在此不再赘述。
代码详解
resolve()、reject()既可以作为 Promise 实例的方法也可以作为 Promise 类的方法。这两种情况我们来对比看看。
例 1 function foo(flag) {if (flag) {return new Promise((resolve) {// 这里可以做异步操作resolve(success);});// return Promise.resolve(success2);} else {return new Promise((reslove, reject) {// 这里可以做异步操作reject(fail);});}}// 执行 reslove 的逻辑foo(true).then((res) {console.log(res);});// 执行 reject 的逻辑foo(false).catch((err) {console.log(err);});
例 2见证奇迹的时刻 function foo(flag) {if (flag) {// Promise的静态方法直接返回字符串return Promise.resolve(success);} else {// Promise的静态方法直接返回字符串return Promise.reject(fail);}}// 执行 reslove 的逻辑foo(true).then((res) {console.log(res);});// 执行 reject 的逻辑foo(false).catch((err) {console.log(err);});
例 1 和例 2 的打印结果是一样的。这两段代码的区别在于例 1 里面可以封装异步任务例 2 只能单纯的返回一个字符串等变量不能封装异步任务。
Promise.all()
Promise.all()的参数是一个数组数组里可以填写多个 PromisePromise.all()的返回值是一个新的 Promise。这里我们以三个 Promise 为例比如 Promsie.all([p1, p2, p3])。它的作用是将p1、p2、p3 这三个 Promise 包裹在一起组成一个新的 Promise。
新 Promise 的状态由 p1、p2、p3 这三个 Promse 共同决定 当 p1、p2、p3等所有的 Promise 状态都变为 fulfilled 时新的 Promise 将变为 fulfilled 状态并会将 p1、p2、p3 等所有 Promise 的返回值组成一个数组作为 then() 的参数。 当p1、p2、p3 等 Promise中有一个 Promise 状态为 rejected 时新的 Promise 将立马变为 rejected 状态并会将第一个 reject() 的返回值作为 catch() 的参数。
Promsie.all([p, p2, p3]) 的使用场景并发处理多个异步任务所有任务都执行成功才算成功才会走到 then只要有一个任务失败就会马上走到 catch整体都算失败。参数里传的是多个 Promise 实例组成的数组。
Promsie.all() 在实际开发中使用得非常频繁真的很好用。我们在开发一个前端页面时经常需要同时调用多个接口等待这些接口的数据都准备好之后前端再来做接下来的事。如果你也遇到这样的需求那么 Promsie.all() 适合你。
语法举例
1、异步任务都执行成功时 const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);resolve(promise 1 成功);}, 1000);});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);resolve(promise 2 成功);}, 2000);});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);});Promise.all([promise1, promise2, promise3]).then((res) {// 三个异步任务都执行成功才会走到这里// 这里拿到的 res是三个成功的返回结果组成的数组console.log(all promise res: JSON.stringify(res));}).catch((err) {// 只要有一个异步任务执行失败就会马上走到这里console.log(err);});
打印结果 // 1秒后执行 promise1// 2秒后执行 promise2// 3秒后执行 promise3all promise res:[promise 1 成功,promise 2 成功,promise 3 成功]
2、异步任务有至少一个执行失败时 const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);resolve(promise 1 成功);}, 1000);});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);// 这里通过 reject() 的方式表示任务执行失败reject(promise 2 失败);}, 2000);});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);});Promise.all([promise1, promise2, promise3]).then((res) {// 三个异步任务都执行成功才会走到这里console.log(走到 then: JSON.stringify(res));}).catch((err) {// 只要有一个异步任务执行失败就会马上走到这里console.log(走到 catch: err);});
打印结果
// 1秒后
执行 promise1// 2秒后
执行 promise2
走到 catch:promise 2 失败// 3秒后
执行 promise3
可以看到当 promise2 执行失败之后马上就走到了 catch获取到了 promise2 失败的结果。
要注意的是promise1、promise3并不会执行 resolve()它俩状态是 pending且无法获取它俩的结果。我们只知道整体的任务是失败的获取了整体的失败结果。
Promise.all()案例多张图片上传
案例现在有一个图片上传的接口每次请求接口时只能上传一张图片。需求是当用户连续上传完九张图片正好凑齐九宫格之后给用户一个“上传成功”的提示。这个时候我们就可以使用Promsie.all()。
这个例子在实际的项目开发中经常遇到属于高频需求需要记住并理解。
1、代码举例如下
const imgArr [1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg, 6.jpg, 7.jpg, 8.jpg, 9.jpg];
const promiseArr [];
imgArr.forEach((item) {const p new Promise((resolve, reject) {// 在这里做图片上传的异步任务。图片上传成功后接口会返回图片的 url 地址// upload img return imgUrlif (imgUrl) {// 单张图片上传完成resolve(imgUrl);} else {reject(单张图片上传失败);}});promiseArr.push(p);
});
Promise.all(promiseArr).then((res) {console.log(图片全部上传完成);console.log(九张图片的url地址组成的数组 res);}).catch((res) {console.log(部分图片上传失败);});
2、上方代码解释
1只有九张图片都上传成功才会走到 then。
第一张图会成功调 upload 接口并返回 imgUrl但不会走到 resolve因为要等其他八张图的执行结果再决定是一起走 resolove 还是一起走 reject。
2按时间顺序来看假设第一张图片上传成功第二张图片上传失败那么最终的表现是 对于前端来说九张图都会走到 reject整体会走到 catch不会走到 then。 对于后端来说第一张图片会上传成功因为写入 DB 是不可逆的第二张图上传失败剩下的七张图会正常请求 upload img 接口。
其实九张图的 upload img 请求都已经发出去了。对于后端来说是没有区别的而且读写 DB 的操作不可逆只是在前端的交互表现不同、走到 resolve / reject / then / catch 的时机不同而已。
3、思维拓展 拓展 1如果你希望九张图同时上传并且想知道哪些图上传成功、哪些图上传失败则可以这样做无论 upload img 接口请求成功与否全都执行 resolve。这样的话最终一定会走到 then然后再根据接口返回的结果判断九张图片的上传成功与否。 拓展 2实战开发中在做多张图片上传时可能是一张一张地单独上传各自的上传操作相互独立。此时 Promise.all便不再适用这就得具体需求具体分析了。 注意某个任务失败之后其他任务会继续执行
一定要注意当执行 Promise.all() / Promise.race() / Promise.any() 等方法时如果其中一个任务失败了其他任务并没有停止会继续执行。只是前端拿不到其他任务的执行状态而已。
其他任务是否需要做一些特殊梳理就要结合你自己的业务逻辑来考虑。
Promse.allSettled()
Promise.all()方法组成的多个Promise中有个明显的特点是只要有一个 Promise 元素进入 rejected 状态则整体的 Promise 会立即进入 rejected 状态。其他 Promise 元素会处于 pending 状态任务本身是否执行成功我们在前端代码里无从知晓因为无法拿到处理结果。我们只知道整体的 Promise 是 fulfilled或者 rejected 获取整体的成功/失败结果。
如果你认为 Promise.all() 的这一点无法满足你的需求那么 Promise.allSettled() 可以提供一种新思路。
Promise.allSettled() 是ES11ES 2020中提供的新API。它会等待所有的 Promise 元素都有结果无论是 fulfilled还是rejected后才会有最终的结果settled而且状态一定是 fulfilled。
Promise.allSettled() 的状态为 fulfilled不代表 里面的 Promise 元素都是 fulfilled这只是在表明里面的 Promise 元素都已经有了就结果可能成功、可能失败。
语法举例
const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);resolve(promise 1 成功);}, 1000);
});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);reject(promise 2 失败);}, 2000);
});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);
});Promise.allSettled([promise1, promise2, promise3]).then(res {// 注意看 res 的返回结果console.log(allSettled:, res);
});
打印结果
执行 promise1执行 promise2执行 promise3allSettled:[{status: fulfilled,value: promise 1 成功},{status: rejected,reason: promise 2 失败},{status: fulfilled,value: promise 3 成功}
]
打印结果截图 从上面的打印结果可以看出Promise.allSettled() 的状态为 fulfilled后then()的回调函数里res 是一个数组数组里存放了每个 Promise 元素的执行结果包括状态和返回值。
在实际开发中Promise.all() 比 Promise.allSettled() 用得更多一些。
Promise.race()
Promise.race([p1, p2, p3])参数里传的是多个 Promise 元素组成的数组。可以并发处理多个Promise整体的执行状态取第一个执行完成的 Promise的状态且状态和第一个完成的任务状态保持一致。
上面这句话第一次读时可能很绕口。我以异步任务为例说的再通俗一点在多个同时执行的异步任务中等待哪个任务 最先执行完成无论是走到 resolve还是走到 reject都算执行完成整体的状态就立即跟这个任务保持一致。如果这个任务执行成功那整体就算成功走到 then如果这个任务执行失败那整体就算失败走到 catch。
race的中文翻译可以理解为“竞赛”、“竞争”。意思是谁先抢到名额就认定谁了。谁前有结果就用谁的结果。无论这个人最终的结局是成功或者失败整体的结局都以这个人的结局为准。
我刚开始学 Promise.race()的时候误以为它的含义是“只要有一个异步执行成功整体就算成功走到 then所有任务都执行失败整体才算失败走到 catch”。现在想来真是大错特错过于懵懂。
现在我顿悟了准确来说Promise.race()强调的是只要有一个异步任务执行完成整体就是完成的。
Promise.race()的应用场景在众多 Promise 实例中最终结果只取一个 Promise 的状态谁返回得最快就用谁的 Promise 状态。
我们来看看各种场景的打印结果继续前行。
语法举例
场景 1、所有任务都执行成功时
const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);resolve(promise 1 成功);}, 1000);
});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);resolve(promise 2 成功);}, 2000);
});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);
});Promise.race([promise1, promise2, promise3]).then((res) {// 第一个完成的任务如果执行成功就会走到这里// 这里拿到的 res是第一个成功的 promise 返回的结果不是数组console.log(JSON.stringify(res));console.log(走到then: res);}).catch((err) {// 第一个完成的任务如果执行失败就会走到这里console.log(err);});
打印结果
// 1秒后
执行 promise1
走到then:promise 1 成功// 2秒后
执行 promise2// 3秒后
执行 promise3
场景 2、第一个任务成功、第二个任务失败时
const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);resolve(promise 1 成功);}, 1000);
});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);// 第二个任务执行失败时reject(promise 2 失败);}, 2000);
});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);
});Promise.race([promise1, promise2, promise3]).then((res) {// 第一个完成的任务如果执行成功就会走到这里console.log(走到then: res);}).catch((err) {// 第一个完成的任务如果执行失败就会走到这里console.log(走到catch: err);});
打印结果
// 1秒后
执行 promise1
走到then:promise 1 成功// 2秒后
执行 promise2// 3秒后
执行 promise3
可以看出场景 2 的打印结果和场景 1 的打印结果是一样的。因为第一个执行完成的任务是成功的所以整体就算成功马上走到 then()。
场景 3、第一个任务失败、第二个任务成功时
const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);// 第一个任务执行失败时reject(promise 1 失败);}, 1000);
});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);resolve(promise 2 成功);}, 2000);
});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);
});Promise.race([promise1, promise2, promise3]).then((res) {// 第一个完成的任务如果执行成功就会走到这里console.log(走到then: res);}).catch((err) {// 第一个完成的任务如果执行失败就会走到这里console.log(走到catch: err);});
打印结果
// 1秒后
执行 promise1
走到catchpromise 1 失败// 2秒后
执行 promise2// 3秒后
执行 promise3
看清楚了没场景 3 的最终打印结果是走到了 catch任务 2 和任务 3 里的 resolve并没有执行。
场景 3 的代码一定要好好理解。
Promise.race()举例图片加载超时
现在有个需求是这样的前端需要加载并显示一张图片。如果图片在三秒内加载成功那就显示图片如果三秒内没有加载成功那就按异常处理前端提示“加载超时”或者“请求超时”。
代码实现
// 图片请求的Promise
function getImg() {return new Promise((resolve, reject) {let img new Image();img.onload function () {// 图片的加载是异步任务resolve(img);};img.src https://img.smyhvae.com/20200102.png;});
}// 加载超时的 Promise
function timeout() {return new Promise((resolve, reject) {// 采用 Promise.race()之后如果 timeout() 的 promise 比 getImg() 的 promise先执行说明定时器时间到了那就算超时。整体的最终结果按失败处理。setTimeout(() {reject(图片加载超时);}, 3000);});
}Promise.race([getImg(), timeout()]).then((res) {// 图片加载成功console.log(res);}).catch((err) {// 图片加载超时console.log(err);});
如代码注释所述采用 Promise.race() 之后如果 timeout() 的 Promise 比 getImg() 的 Promise 先执行说明定时器时间到了那就算超时。整体的最终结果按失败处理。
这个思路很巧妙。用同样的思路我们还可以处理网络请求超时的问题。如果接口请求时长超过 3 秒就按超时处理也就是下面我们要举的例子。
Promise.race()举例网络请求超时
现在有这种需求如果接口请求时长超过 3 秒就按超时处理。
基于这种需求我们可以用 Promise.race() 来实现一个 Promise 用于请求接口另一个 Promise 用于 setTimeout() 定时器。把这两个 Promise 用 Promise.race() 组装在一起谁先执行那么最终的结果就以谁的为准。
代码举例
function query(url, delay 4000) {let promiseArr [myAajax(url),new Promise((resolve, reject) {setTimeout(() {reject(网络请求超时);}, delay);}),];return Promise.race(promiseArr);
}query(http://localhost:8899/xxx_url, 3000).then((res) {console.log(res);}).catch((error) {console.log(error);});
Promise.any()
Promise.any() 是 ES12ES 2021中推出的新API。它类似于 Promise.race()但有一个关键的区别Promise.any() 会等待参数中第一个状态为 fulfilled 的Promise元素然后立即进入 fulfilled状态。
如果参数中所有的 Promise 元素都进入了 rejected那么也会等到所有的Promise都变成rejected 状态最终报错 AggregateError。
语法举例
场景1、第一个任务失败第二个任务成功
const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);reject(promise 1 失败);}, 1000);
});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);resolve(promise 2 成功);}, 2000);
});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);resolve(promise 3 成功);}, 3000);
});Promise.any([promise1, promise2, promise3]).then(res {console.log(走到then:, res);
});
打印结果
// 1秒后
执行 promise1// 2秒后
执行 promise2
走到then(): promise 2 成功// 3秒后
执行 promise3
场景2、三个任务都失败
const promise1 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise1);reject(promise 1 失败);}, 1000);
});const promise2 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise2);reject(promise 3 失败);}, 2000);
});const promise3 new Promise((resolve, reject) {setTimeout(() {console.log(执行 promise3);reject(promise 3 失败);}, 3000);
});Promise.any([promise1, promise2, promise3]).then(res {console.log(走到then:, res);}).catch(err {console.log(走到catch:, err);});
打印日志
// 1秒后
执行 promise1// 2秒后
执行 promise2// 3秒后
执行 promise3
走到catch: AggregateError: All promises were rejected
注意看打印结果中的报错信息。执行 promise3这行日志出来之后报错的那行马上就出来了。
兼容性问题
Promise.any() 方法依然是实验性的尚未被所有的浏览器完全支持。它当前处于 TC39 第四阶段草案Stage 4。
总结
Promise 的静态方法简化处理了多个并发操作的代码使其更加方便、直观地调用。
Promise 不仅能解决嵌套异步任务的回调地域问题也可管理多个异步任务的并发请求。
Promise 本身不是异步的但是它可以封装异步任务并对异步操作进行良好的、舒适简洁的状态管理这便是 Promise 的魅力所在。
赞赏作者
创作不易你的赞赏和认可是我更新的最大动力