深入了解 promise

247 阅读6分钟

ES6 之前的异步解决方案

在了解promise前先了解一下ES5之前是怎么处理异步任务的

用一个网络请求的例子
函数中发送网络请求;
发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去;
发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息;

function requestDate(value,successCallback,failureCallback){
  setTimout(() => {
     if(value){
        successCallback("获取成功接收到的数据")
     }else{
        failureCallback("错误信息")
     }
  })
}

这代码看起来也不复杂,但当我们需要发送多个异步请求,并且每个请求之间需要相互依赖,那时,我们只能 以嵌套方式来解决,从而就形成回调地狱
回调地狱指的是多个函数内嵌而导致的一层套一层的函数结构,阅读效果差,会导致多种问题,在异步操作需要变更时,需要大规模的代码重构。

而promise的出现,就是为了解决原有技术上的痛点

Promise是什么?

抽象理解Promise 是 JS 中进行异步编程的新的解决方案
具体理解从语法上来说:promise 是一个构造函数,给我们调用者一个承诺,承诺返回一个结果

promise 的基本使用

在通过new Promise(),我们需要传入一个回调函数,我们称之为executor

  • 这个回调函数会被立即执行(同步),并且给传入另外两个回调函数resolve、reject;
  • 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
  • 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
const promise = new Promise((resolve,reject)=>{
  //resolve();
  //reject();
})
promise.then((res)=>{...}).catch((err)=>{...})

executor(执行器)

executor是在创建Promise时需要传入的一个回调函数,这个回调函数是同步的会被立即执行,并且传入两个参数。
在executor函数中promise有三种状态:

  • 在未等待状态改变之前属于 pending 状态
  • 在执行了resolve,则属于成功 fulfilled 状态
  • 当执行了reject,则属于失败 reject 状态 注意点:promise的状态一旦确定下来,无论是成功还是失败,都会有一个结果数据,无法更改,在执行了resolve之后再调用reject,此时状态是不会变更的。

resolve参数的区别

传入一个普通的值或者对象,那么这个值会作为then回调的参数;

new Promise((resolve,reject)=>{
   resolve({})//(Number|String|Object|undefinde)
}).then(res =>{
   console.log("res":res)//"res":{}
})

传入的是一个Promise,那么这个新Promise会决定原Promise的状态;

new Promise((resolve,reject) =>{
  resolve(new Promise(resolve,reject) =>{
     setTimeout(() =>{
       resolve("返回promise")
     },1000)
  })
}).then(res =>{
  console.log("res:",res)// "res:",返回promise
})

传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据 then方法的结果来决定Promise的状态;

new Promise((resolve,reject)=>{
   resolve({
      then:function(resolve,reject){
        resolve("返回promise")
      }
   })
}).then(res =>{
   console.log("res:",res)// "res:",返回promise
})

then方法

then方法是Promise原型对象上的一个方法,根据上一个成功的结果执行,接受两个参数:

  • 第一个参数是fulfilled的回调函数
  • 第二个参数是preject的回调函数
new Promise((resolve,reject)=>{
   resolve()
}).then(res =>{
  console.log(res)
},err =>{
  console.log(err)
})

返回值
then方法本身是有返回值的,它的返回值是一个Promise,所以我们可以进行链式调用
当then方法中的回调函数返回一个结果时,那么它处于fulfilled状态,并且会将结果作为resolve的参数;
当then方法抛出一个异常时,那么它处于reject状态;
返回值是一个普通的值

//当返回的是一个普通值, 那么这个普通的值被作为一个新的Promise的resolve值
promise.then(res => {
   return 100; //(数值/字符串/普通对象/undefined)
}).then(res => {
  console.log("res:", res)
  return 120;
})

返回值是一个Promise

// 当返回的是一个Promise
promise.then(res => {
  return new Promise((resolve, reject) => {
   setTimeout(() => {
      resolve(100)
    }, 3000)
  })
}).then(res => {
   console.log("res:", res)
})

返回值一个包含then方法的值

//当返回的是一个对象, 并且该对象实现了thenable
promise.then(res => {
  return {
    then: function(resolve, reject) {
      resolve(100)
    }
  }
}).then(res => {
  console.log("res:", res)
})

catch

catch等同于then第二个参数

promise.catch(err => {
  console.log("err:", err)
})
//等同于 
promise.then(null,()=>{})

返回值catch方法也是会返回一个Promise对象的,所以catch方法后面也可以调用then方法或者catch方法


new Promise((resolve,reject)=>{
   reject()
}).then(res =>{
  console.log("res:",res)
}).catch(err =>{
  console.log("err:",err)//err: 1000
}).then(res => {
  console.log("res0:",res)//res0: undefined
}).catch(err =>{
  console.log("err0:",err)
})
//抛出错误
new Promise((resolve,reject)=>{
   reject()
}).then(res =>{
  console.log("res:",res)
}).catch(err =>{
  console.log("err:",err)//err: 1000
  return new Error("try err")
}).then(res => {
  console.log("res0:",res)
}).catch(err =>{
  console.log("err0:",err)//res0: Error: try err
})

finally

finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会被执行的代码
finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行

new Promise((resolve,reject)=>{
   reject(123)
}).then(res =>{
  console.log("res:",res)
}).catch(err =>{
  console.log("err:",err)
}).finally(()=>{
  console.log("finally")
})

resolve

resolve方法是Promise类直接调用,Promise.resolve();
同样参数拥有

  • 返回一个普通的值;
  • 返回一个Promise;
  • 返回一个thenable值;
Promise.resolve()
//等同
new Promise((resolve,reject)=>{
   resolve()
})

reject

reject方法Promise类直接调用,Promise.reject();

Promise.reject()
//等同
new Promise((resolve,reject)=>{
   reject()
})

all

作用:将多个Promise包裹在一起形成一个新的Promise
新的Promise状态由包裹的所有Promise共同决定:

  • 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
  • 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;
    all()方法Promise类直接调用,Promise.all;
const p0 = new Promise((resolve,reject)=>{...})
const p1 = new Promise((resolve,reject)=>{...})
const p2 = new Promise((resolve,reject)=>{...})

const p3 = Promise.all([p0,p1,p2])
.then(res =>{
   console.log(res)
})
.catch(err =>{
   console.log(err)
})

allSettled

all方法有一个缺陷:当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态;
但对于resolved,以及依然处于pending状态的Promise,我们是获取不到对应的结果的;
在ES11(ES2020)中,添加了新的API Promise.allSettled:

  • 该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
  • 并且这个Promise的结果一定是fulfilled的;
const p0 = new Promise((resolve,reject)=>{resolve(123)}) 
const p1 = new Promise((resolve,reject)=>{resolve(true)})
const p2 = new Promise((resolve,reject)=>{reject(false)})
Promise.allSettled([p0,p1,p2])
.then(res =>{ console.log(res) })
.catch(err =>{ console.log(err) })
//pallSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;
//这个对象中包含status状态,以及对应的value值;
(3) [{…}, {…}, {…}]
  0: {status'fulfilled'value123}
  1: {status'fulfilled'valuetrue}
  2: {status'rejected'reasonfalse}

race

race表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果

const p0 = new Promise((resolve,reject)=>{resolve(123)})
const p1 = new Promise((resolve,reject)=>{resolve(true)})
const p2 = new Promise((resolve,reject)=>{reject(false)})
Promise.race([p0,p1,p2])
.then(res =>{
   console.log(res)
})
.catch(err =>{
   console.log(err)
})

any

any方法是ES12中新增的方法,和race方法是类似的:

  • any方法会等到一个fulfilled状态,才会决定新Promise的状态;
  • 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;
  • 如果所有的Promise都是reject的,那么会报一个AggregateError的错误。
const p0 = new Promise((resolve,reject)=>{reject(123)})
const p1 = new Promise((resolve,reject)=>{reject(true)})
const p2 = new Promise((resolve,reject)=>{reject(false)})
Promise.any([p0,p1,p2])
.then(res =>{
   console.log(res)
})
.catch(err =>{
   console.log(err) //AggregateError: All promises were rejected
})