Promise的原理即用法

100 阅读4分钟

Callback 的异步操作形式除了会造成回调地狱,还会造成难以测试的问题。ES6 中的 Promise (基于 Promise A + 规范的异步编程解决方案)利用有限状态机的原理来解决异步的处理问题,Promise 对象提供了统一的异步编程 API,它的特点如下:

  • Promise 对象的执行状态不受外界影响。Promise 对象的异步操作有三种状态: pending(进行中)、 fulfilled(已成功)和 rejected(已失败) ,只有 Promise 对象本身的异步操作结果可以决定当前的执行状态,任何其他的操作无法改变状态的结果
  • Promise 对象的执行状态不可变。Promise 的状态只有两种变化可能:从 pending(进行中)变为 fulfilled(已成功)或从 pending(进行中)变为 rejected(已失败)

promise是用来解决两个问题的:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据,这个promise可以解决异步的问题,本身不能说promise是异步的

resolve

const getData = ()=>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('执行完成Promise');
        resolve('要返回的数据可以任何数据例如接口返回数据');
    }, 2000);
  })
}

getData().then((e)=>{
  console.log(e)//要返回的数据可以任何数据例如接口返回数据
})

reject

const getData = ()=>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('执行完成Promise');
        reject('错误啦');
    }, 2000);
  })
}

getData().then((e)=>{
  console.log(e)
}).catch((error)=>{
  console.log(error)//错误啦
})

Promise.all()

可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promise.reject('失败')

let p4 = Promise.reject('faild')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2,p4]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)                 // '失败'
})

Promise.all([p1,p4,p2,p3]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)                 // 'faild'
})

只有p1、p2、p3、p4的状态都变成fulfilled,p(总的)的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数

只要p1、p2、p3、p4之中有一个被rejected,p(总的)的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

Promise.race()

all是等所有的异步操作都执行完了再执行then方法,那么race方法就是相反的,谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },200)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 'success'
})

就是赛跑,比谁速度快。

Promise.allSettled()

p1,p2,p3,p4实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

所以,Promise.all()无法确定所有请求都结束,如果你需要知道所有入参的异步操作的所有结果,或者需要知道这些异步操作是否全部结束,应该使用promise.allSettled()

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promise.reject('失败')

let p4 = Promise.reject('faild')

Promise.allSettled([p1, p2,p3,p4]).then((result) => {
  console.log(result) 
  //返回数组 {status: 'fulfilled', value: '成功了'},{status:'fulfilled', value: 'success'}...
}).catch((error) => {
  // console.log(error)      
})

总结下

  • Promise.all:适合多个异步任务并发执行但不允许其中任何一个任务失败
  • Promise.race :适合多个异步任务抢占式执行
  • Promise.allSettled :适合多个异步任务并发执行但允许某些任务失败