Promise详解与应用

153 阅读5分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

前言

Promise,可以说是ES6中相当重要的一个概念了,在面试中出现的频率也是非常高。一个Promise可以衍生出N道面试题,那如此多的题目该怎么去应对呢,不要慌,万变不离其宗,题目再怎么多,始终还是Promise。所以当我们把Promise搞清楚之后,不管面试官怎么问,我们也能从容应对了。

要搞清楚Promise,首先我们得知道Promise是什么,应该怎么用,它有什么优缺点,知道了这些,最起码对于初中级前端应该能够应对大部分Promise面试题了。至于它的原理,实现方式等我们后面再说。

Promise是什么

Promise,英文翻译过来就是“承诺”,“许诺”的意思,就是我承诺你一定会帮你做这件事,要么就没开始做,要么已经开始了,而事情做完也只有两种结果,要么成功,要么失败。在ES6中,Promise是异步编程的一种解决方案,它是一个对象,它可以获取异步操作的结果。

Promise有以下两种特点:

  1. 对象状态不受外界影响。“就算上刀山,下火海,我承诺你的事情也一定会做到”,Promise代表一个异步操作,它有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败)。只有异步操作的结果决定了当前是何种状态,其他无论什么操作都改变不了。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise的状态改变只有两种情况:pending=>fulfilledpending=>rejected。还是那句话,要么成功,要么失败,无论是变成fulfilled还是变成rejected,结果一旦确定了就不会变了。另外,如果结果已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。它跟事件不同,事件你错过了再去监听,就得不到结果了。

Promise怎么用

基本用法

let promise = new Promise(function(resolve,reject) {
    if(/* 操作成功 */){
       resolve(value); 
    }else{
       reject(error);
    }
})

Promise构造函数接受一个函数作为参数,该函数分别有两个参数resolverejectresolve函数是将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方法的回调函数不接受任何参数,所以是不知道前面的Promiseresolved还是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的状态由promise1promise2promise3决定,有两种情况

(1)只有Promise1Promise2Promise3的状态都变成fulfillednewPromise的状态才会变成fulfilled,此时Promise1Promise2Promise3的返回值组成一个数组,传递给newPromise的回调函数。

(2)只要Promise1Promise2Promise3中有一个被rejectednewPromise的状态就变成rejected,此时第一个被rejected实例的返回值传递给newPromise的回调函数。

Promise.race()

Promise.race()也同样是将多个Promise实例包装成一个新的Promise实例。

const newPromise = Promise.race([promise1, promise2, promise3]);

:只要Promise1Promise2Promise3中有一个实例先改变状态,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优缺点

优点:

  1. 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数;
  2. 提供了统一的API,使各种异步操作都可以用同样的方法进行处理。

缺点:

  1. 无法取消Promise,一旦新建就会立即执行,无法中途取消;
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部;
  3. 当处于pending状态时,无法得知目前进展到哪个阶段(是刚刚开始还是即将完成)