Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
前言
Promise
,可以说是ES6
中相当重要的一个概念了,在面试中出现的频率也是非常高。一个Promise
可以衍生出N道面试题,那如此多的题目该怎么去应对呢,不要慌,万变不离其宗,题目再怎么多,始终还是Promise
。所以当我们把Promise
搞清楚之后,不管面试官怎么问,我们也能从容应对了。
要搞清楚Promise
,首先我们得知道Promise
是什么,应该怎么用,它有什么优缺点,知道了这些,最起码对于初中级前端应该能够应对大部分Promise
面试题了。至于它的原理,实现方式等我们后面再说。
Promise是什么
Promise
,英文翻译过来就是“承诺”,“许诺”的意思,就是我承诺你一定会帮你做这件事,要么就没开始做,要么已经开始了,而事情做完也只有两种结果,要么成功,要么失败。在ES6中,Promise是异步编程的一种解决方案,它是一个对象,它可以获取异步操作的结果。
Promise有以下两种特点:
- 对象状态不受外界影响。“就算上刀山,下火海,我承诺你的事情也一定会做到”,
Promise
代表一个异步操作,它有三种状态:pending
(进行中)、fulfilled
(成功)、rejected
(失败)。只有异步操作的结果决定了当前是何种状态,其他无论什么操作都改变不了。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise的状态改变只有两种情况:
pending
=>fulfilled
,pending
=>rejected
。还是那句话,要么成功,要么失败,无论是变成fulfilled
还是变成rejected
,结果一旦确定了就不会变了。另外,如果结果已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。它跟事件不同,事件你错过了再去监听,就得不到结果了。
Promise怎么用
基本用法
let promise = new Promise(function(resolve,reject) {
if(/* 操作成功 */){
resolve(value);
}else{
reject(error);
}
})
Promise
构造函数接受一个函数作为参数,该函数分别有两个参数resolve
和reject
。resolve
函数是将Promise
的状态从pending
变成resolved
,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。reject
函数是将Promise
的状态从pending
变成rejected
,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise.prototype.then()
Promise
实例具有then
方法,它是为Promise
实例添加状态改变时的回调函数。then
方法返回的是一个新的Promise
实例,因此可以采用链式写法。
fun().then(res => {
//...
}).then()
Promise.prototype.catch()
Promise.prototype.catch()
方法用于指定发生错误时的回调函数。
fun().then(res => {
//...
}).catch(error => {
console.log(eror);
})
Promise.prototype.finally()
finally()
方法用于指定不管Promimse
对象最后状态是什么,都会执行的操作。finally
方法的回调函数不接受任何参数,所以是不知道前面的Promise
是resolved
还是rejected
,这也就意味着finally
里面的操作是与状态无关的。
fun().then(res => {
//...
}).catch(error => {
console.log(eror);
}).finally(() => {
// 最终都会执行该操作。
})
Promise.all()
Promise.all()
方法用于将多个Promise
实例,包装成一个新的Promise
实例。Promise.all()
方法的参数可以是一个数组。如果不是数组,也必须具有Iterator
接口,且返回的每个成员都是Promise
实例。
const newPromise = Promise.all([promise1, promise2, promise3]);
注:newPromise
的状态由promise1
、promise2
、promise3
决定,有两种情况
(1)只有Promise1
、Promise2
、Promise3
的状态都变成fulfilled
,newPromise
的状态才会变成fulfilled
,此时Promise1
、Promise2
、Promise3
的返回值组成一个数组,传递给newPromise
的回调函数。
(2)只要Promise1
、Promise2
、Promise3
中有一个被rejected
,newPromise
的状态就变成rejected
,此时第一个被rejected
实例的返回值传递给newPromise
的回调函数。
Promise.race()
Promise.race()
也同样是将多个Promise
实例包装成一个新的Promise
实例。
const newPromise = Promise.race([promise1, promise2, promise3]);
注:只要Promise1
、Promise2
、Promise3
中有一个实例先改变状态,newPromise
的状态就跟着改变,先改变的实例的返回值,会传递给newPromise的回调函数。
Promise.allSettled()
上面已经说到了Promise.all()
只要有一个失败就整体失败了,这个显然是有点不太人性化的。如果我希望等一组异步操作都结束,我再进行下一步操作,而不管这一组里有没有失败。为了解决这个问题,ES2020
引入了Promise.allSettled()
方法。用来确定一组异步操作是否都结束了(不管成功还是失败)。
Promise.allSettled()
方法同样接受一个Promise
对象数组作为参数,只有等到数组中所有Promise
对象都发生状态变更,返回的Promise
对象才会发生状态变更。
const newPromise = Promise.allSettled([promise1, promise2, promise3]);
Promise.any()
ES2021
引入Promise.any()
方法,该方法接受一组Promise
实例作为参数,包装成一个新的Promise
实例返回。
const newPromise = Promise.any([promise1, promise2, promise3]);
注:只要有一个参数实例变成fulfilled
状态,newPromise
就会变成fulfilled
状态。当所有实例都变成rejected
状态,newPromise
才会变成rejected
状态,这一点正好跟Promise.race()
相反。
Promise.resolve()
Promise.resolve()
用于将现有对象转成Promise
对象。
const myPromise = Promise.resolve($.ajax(''));
Promise.reject()
Promise.reject()
方法返回一个新的Promise实例,该实例的状态为rejected
。
const myPromise = Promise.reject('错误');
Promise优缺点
优点:
- 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数;
- 提供了统一的API,使各种异步操作都可以用同样的方法进行处理。
缺点:
- 无法取消Promise,一旦新建就会立即执行,无法中途取消;
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部;
- 当处于pending状态时,无法得知目前进展到哪个阶段(是刚刚开始还是即将完成)