Promise
1.含义
异步编程解决方案。
Promise,就是一个容器。里面保存着某个未来才会结束的事情。从语法上说。Promise是一个对象从它可以获取异步操作的信息。
特点:
-
对象的状态不受外界影响。
Promise对象代表一个异步操作。三种状态:pending(进行中)fulfilled(已成功)rejected(已失败)
只有异步操作的结果,可以决定当前是哪一种状态。任何其他操作都无法改变
-
一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
缺点:
- 无法取消
Promise,一旦新建就立即执行。无法中途取消。 - 如果不设置回调函数,
Promise内部抛出的错误,不会反映到外部 - 当处于
pending状态时,无法得知目前进展到哪一个阶段
2.基本用法
const promise = new Promise(function(resolve, reject){
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
})
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
-
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; -
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接受两个回调函数作为参数。
-
第一个回调函数是
Promise对象的状态变为resolved时调用, -
第二个回调函数是
Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。它们都接受
Promise对象传出的值作为参数。
function timeout(ms){
return new Promise((resolve, reject) => {
//状态改变只能有一个 resolved或rejected,这里谁在前执行谁
setTimeout(reject, ms, 'err');
setTimeout(resolve, ms, 'done');
})
}
timeout(100).then((value) => console.log(value));
//timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)后,Promise实例的状态改为resolved,就会触发then方法绑定的回调函数。
异步加载图片的例子:
//使用Promise包装了一个图片加载的异步操作。如果加载成功,就调用resolve方法,否则就调用reject方法。
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例,比如像下面这样。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
});
//此时p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。
//这是p1的状态就是传递给p2,也就是说p1的状态决定p2的状态。如果p1的状态是pending,p2的回调函数就会等p1的状态改变,如果p1状态是 resolved或rejected,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的Resolve或Rejected的返回是另一个Promise时,自身的状态由返回的Promise决定。而调用resolve或reject并不会重劫Promise的参数函数的执行。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
//resolved的Promise是本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
所以说可以用Return
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
方法
-
**Promise.prototype.then():**为Promise实例添加状态改变时的回调函数。
then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... });采用链式的
then,可以指定一组按照次序调用的回调函数。这是前一个回调函数有可能返回的还是一个Promise对象(即由异步操作),这是后一个回调函数,就会等该Promise对象的状态发生变化,才会被调用。getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function (comments) { console.log("resolved: ", comments); }, function (err){ console.log("rejected: ", err); }); //第一个then返回的是一个Promise所以,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生改变(Resolved, rejected)去调用相对应的回调函数 //可以更改为箭头函数 getJSON("/post/1.json").then( post => getJSON(post.commentURL) ).then( comments => console.log("resolved: ", comments), err => console.log("rejected: ", err) ); -
**Promise.prototype.catch():**用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); }); //此示例中,getJSON()方法返回的Promise对象,如果状态改为Resolved(),会执行then(),如果状态改为rejected() ,会执行Catch(),并且如果then()的回调函数,运行中抛出错误,也会被catch()方法捕获。 p.then((val) => console.log('fulfilled:', val)) .catch((err) => console.log('rejected', err)); // 等同于 p.then((val) => console.log('fulfilled:', val)) .then(null, (err) => console.log("rejected:", err));Reject()方法的作用等同于抛出错误。js如果Promise状态已经变为Resolved,在抛出错误时无效的const promise = new Promise(function(resolve, reject) { resolve('ok'); throw new Error('test'); }); promise .then(function(value) { console.log(value) }) .catch(function(error) { console.log(error) }); // okPromise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个
catch语句捕获。 -
**Promise.prototype.finally():**不管Promise对象最后状态如何,都会执行的操作。
promise .then(result => {···}) .catch(error => {···}) .finally(() => {···}); -
Promise.all(): 用于将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.all([p1, p2, p3]);p的状态由p1、p2、p3决定,分成两种情况。- 只有
p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 - 只要
p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
// 生成一个Promise对象的数组 const promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json"); }); Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });如果作为参数的Promise实例,自己定义了
catch方法,那么他一旦被rejected,并不会触发Promise.all()的catch方法。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方法指定的回调函数。即当,子Promise中具有自己的Catch()方法,父Promise一定会走进Resolve中
const a1 = new Promise((resolve, reject) => { resolve(1 + 99.12); }); const a2 = new Promise((resolve, reject) => { throw new Error('报错') }).catch(err => console.log(err)); Promise.all([a1, a2]) .then(res => console.log(res)) .catch(e => console.log(e));[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z5yBqRae-1657593353201)(C:\Users\z22011010\AppData\Roaming\Typora\typora-user-images\image-20220712090903635.png)]
- 只有
-
**Promise.race():**同样是将多个Promise实例,包装成一个新的Promise实例,只要有一个实例率先改变状态,p的状态就随之改变,率先改变的Promise实例的返回值,传递给p的回调函数。即多个Promise只要有一个改变状态,就改变p的状态,返回值传给p的回调函数
const r1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1秒成功返回');
}, 1000);
})
const r2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2秒返回成功')
}, 2000);
})
Promise.race([r1, r2]).then(result => console.log(result))
-
**Promise.allSettled():**只有等到参数数组的所有Promise对象都发生状态变更,返回的Promise对象才会发生状态变更。
const s1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('1秒成功返回'); }, 1000); }) const s2 = new Promise((resolve, reject) => { setTimeout(() => { reject('1秒失败返回'); }, 1000); }) const s3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('2秒成功返回'); }, 2000); }) const p = Promise.allSettled([s1, s2, s3]).then(result => console.log(result))[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8zY5cFW-1657593353205)(C:\Users\z22011010\AppData\Roaming\Typora\typora-user-images\image-20220712095024314.png)]
Promise.allSettled()的返回值allSettledPromise,状态只可能变成fulfilled。他的回调函数收到的参数数组results,该数组每个成员都是一个对象,对应传入Promise.allSettled()的数组里面的两个 Promise 对象。
//results的每个成员是一个对象,对象的格式是固定的,对应异步操作的结果
// 异步操作成功时,成功会有value属性
{status: 'fulfilled', value: value}
// 异步操作失败时,失败会有reason属性
{status: 'rejected', reason: reason}
-
**Promise.any():**接收一组Promise实例作为参数,包装成一个新的Promise实例返回。
只要参数实例有一个变成
fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。const an1 = new Promise((resolve, reject) => { setTimeout(() => { reject('1秒失败返回'); }, 1000); }) const an2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('2秒成功返回'); }, 2000); }) Promise.any([an1, an2]) .then(result => console.log(result)) //只要有一个参数实例状态变为fulfilled .catch(error => console.log(error)) //只有所有的参数实例状态都变成rejected

746

被折叠的 条评论
为什么被折叠?



