一、Promise的一些方法
1.Promise.race()
(1)简介:Promise.race()方法是将多个Promise实例,包装成一个新的Promise实例
const p = Promise.race([p1, p2, p3]);
只要p1, p2, p3之中有一个实例的状态率先发生改变,p的状态就跟着改变。
那率先改变的Promise实例的返回值,就传递给p的回调函数。
(2)参数:和Promise.all()的参数一样,是一个元素为Promise对象的数组
(3)返回值:一个新的Promise对象
(4)注意点:
- Promise.race()方法如果传入的参数不是Promise实例,就会先调用Promise.resolve()方法,将参数转成Promise实例,再进一步处理。
- 只要参数中有其中的一个Promise状态发生改变,新生成的Promise对象的状态也会随之发生改变。下面的例子就是这样:
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。
2.Promise.allSettled()
(1)简介:Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
上面代码对服务器发出三个请求,等到三个请求都结束,不管请求成功还是失败,加载的滚动图标就会消失。
(2)参数:和Promise.all()的参数一样,是一个元素为Promise对象的数组
(3)返回值:一个新的Promise对象
(4)和Promise.all()区别:
- Promise.allSettled()必须要等到所有传入的Promise状态变为fullfilled还是rejected,才能返回一个Promise对象。Promise.all()返回Promise对象的条件有二:一是所有的Promise的状态都变为fullfilled,二是其中的一个Promise的状态变为rejected
- Promise.allSettled()的返回值allSettledPromise,状态只可能变成fulfilled。Promise.all()则是要求传入的所有Promise对象的状态都变为fullfilled时,返回的对象的状态才能为fullfilled,如果其中的一个Promise对象的状态变为rejected,返回的对象的状态就会变为rejected。
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
//allSettledPromise的状态只可能变成fullfilled
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
(5)使用场景
有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。比如在请求某些图片的过程中显示加载中...,这时候我们并不去关心图片究竟有没有成功返回,我们只需要关心请求这个操作是否结束。这时,Promise.allSettled()方法就很有用。如果没有这个方法,想要确保所有操作都结束,就很麻烦。Promise.all()方法就不能做到这一点。请看下面的例子:
const urls = [ /* ... */ ];
const requests = urls.map(x => fetch(x));
try {
await Promise.all(requests);
console.log('所有请求都成功。');
} catch {
console.log('至少一个请求失败,其他请求可能还没结束。');
}
上面代码中,Promise.all()无法确定所有请求都结束。想要达到这个目的,写起来很麻烦,有了Promise.allSettled(),这就很容易了。
3.Promise.any()
(1)简介:Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
请看下面的这个例子:
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);
Promise.any([resolved, rejected, alsoRejected]).then(function (result) {
console.log(result); // 42
});
Promise.any([rejected, alsoRejected]).catch(function (results) {
console.log(results); // [-1, Infinity]
});
(2)参数:和Promise.all()的参数一样,是一个元素为Promise对象的数组
(3)返回值:一个新的Promise对象
(4)区别:
- 和Promise.all()的区别:可以说是和Promise.all()刚好相反,Promise.all()是所有的Promise的状态都变为fullfilled时,返回的Promise对象的状态才会变成fullfilled,但是如果其中一个Promise状态变为rejected,返回的Promise的状态就马上变为rejected。
- 和Promise.race()的区别:Promise.any()跟Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。而Promise.race()如果有其中的一个Promise对象的状态改变,返回的Promise对象的状态将会随着改变的Promise对象的状态的改变而改变。
4.Promise.try()
实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。
Promise.resolve().then(f)
上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行。
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
如果想要解决这个问题,可以使用Promise.try对f进行包装,这样就可以保证同步函数同步执行,异步函数异步执行。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next
另外,Promise.then()虽然可以捕获Promise对象抛出的异步错误,但是却捕获不了同步错误,看下面的例子:
function getUsername(userId) {
return database.users.get({id: userId})
.then(function(user) {
return user.name;
});
}
.catch((e) => console.log(e));
上面代码中,database.users.get()返回一个 Promise 对象,如果抛出异步错误,可以用catch方法捕获.
但是,如果database.users.get()抛出了同步错误(比如数据库链接错误等),catch就不能捕获到这个错误,这时就不得不使用try...catch去捕获,就像这样:
try {
database.users.get({id: userId})
.then(...)
.catch(...)
} catch (e) {
// ...
}
但是,如果使用Promise.try()就可以统一使用catch捕获同步或者是异步错误
Promise.try(() => database.users.get({id: userId}))
.then(...)
.catch(...)
我的理解就是Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。
5.Promise.resolve()
(1)简介:Promise.resolve()将现有的对象或其他转化为一个Promise对象。下面是一个使用Promise.resolve()的例子:
const jsPromise = Promise.resolve($.ajax('/whatever.json')); //jsPromise是一个Promise对象
上面代码将jQuery生成的deferred对象转成一个新的Promise对象。
Promise.resolve()等价于下面的写法:
Promise.resolve('foo')
//等价于
new Promise((resolve) => resolve('foo'));
(2)参数:
- 参数是一个Promise实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
- 参数是一个thenable对象
thenable对象是指具有then方法的对象,比如下面这个对象。
//这是一个thenable对象
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
//使用Promise.resolve()方法
let p1 = Promise.resolve(thenable); //会立即执行then方法
p1.then(function(value) {
console.log(value); // 42
});
注意:Promise.resolve()方法会立即执行thenable对象里面的then方法,然后将对象p1的状态变为resolved,从而执行p1.then()方法,输出42.
- 参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello'); //p的状态一定会变成resolved
p.then(function (s){
console.log(s)
});
// Hello
上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数。
- 不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
const p = Promise.resolve(); //p的状态一定会变成resolved
p.then(function () {
// ...
});
注意:立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行(微任务),而不是在下一轮“事件循环”的开始时。
//setTimeout是宏任务,在下一轮事件循环才执行
setTimeout(function () {
console.log('three');
}, 0);
//微任务,在本轮事件循环结束的时候执行
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。
6.Promise.reject()
(1)简介:Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。
(2)注意:
注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
})
// true
上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。