ES6 Promise有几种写法以及如何理解

写在前面

感性上的理解:Promise我们可以理解为做了一个保证,做了这个保证不管成功resolve()还是失败reject()都会告知我们———返回带有最终结果或者拒绝原因 的Promise

Promise的三种状态

  • 挂起
  • 已成功
  • 已完成

其中后两种都是异步操作完成后的状态

Promise做保证

Promise对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。

MDN对Promise的定义如上,可以理解为此对象做了一些保证,告知我们异步操作的状态。具体如下:

  • 当前事件队列执行完成之后,再调用回调函数
  • 回调函数是通过then添加的
  • 添加多个then,可以添加多个回调函数,依次执行

Promise链式调用

存在的需求:有时候我们需要连续调用多个异步操作,每一个操作都建立在得到上一部结果之后。以前有回调函数,这样会容易造成回调地狱。而采用Promise之后,每一步操作成功/失败之后都会带上其结果,执行下一个操作。

// 回调地狱
doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
        console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

//采用Promise链式调用之后
doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

错误处理

在上面的代码段1中,有三个错误回调函数。而在Promise链式调用中,只在结尾加上错误处理回调即可,当有错误抛出时,会返回一个带有错误原因的Promise到下一级catch,直到最底端catch函数。

// 依次输出: Initial Do that
new Promise((resolve, reject) => {
    console.log('Initial');
    resolve();
})
.then(() => {
    throw new Error('Something failed');
    console.log('Do this');
})
.then(() => {
    console.log('Do this whatever happened before');
})
.catch(() => {
    console.log('Do that');
})

此外,如果中途捕获了异常,依然可以接着then下去:

/*
 * 依次输出:
 * Initial
 * Do that
 * Do this whatever happened before
 */
new Promise((resolve, reject) => {
    console.log('Initial');
    resolve();
})
.then(() => {
    throw new Error('Something failed');
    console.log('Do this');
})
.catch(() => {
    console.log('Do that');
})
.then(() => {
    console.log('Do this whatever happened before');
})

原因在于catch(failureCallback)本身是then(null, failureCallback)的缩略形式,也是返回带有当前状态的Promise。下面这样咱们还能捕获到异常信息:

/*
 * 依次输出:
 * Initial
 * Something failed
 * Do that
 * Do this whatever happened before
 */
new Promise((resolve, reject) => {
    console.log('Initial');
    resolve();
})
.then(() => {
    throw new Error('Something failed');
    console.log('Do this');
})
.catch((e) => {
    console.log(e.message)
    console.log('Do that');
})
.then(() => {
    console.log('Do this whatever happened before');
})

使用async/await语法糖

一个栗子

// 使用Promise
doSth()
    .then(res => doSthElse(res))
    .then(newRes => doThirdTh(newRes))
    .then(finalRes => {
        console.log(`finalResult is ${finalRes}`)
    })
// 使用async/await将异步代码写成同步样式
async funtion foo () {
    let res = await doSth()
    let newRes = await doSthElse(res)
    let finalRes = await doThirdTh(newRes)
    console.log(`finalResult is ${finalRes}`)
}

Promise.resolve()、Promise.reject()妙用

使用这两种静态方法可以创建resolve或reject的保证,栗子如下:

getRecommend () {
    let today = new Date()
    let date = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate(), 9)
    
    return axios.get(`/api/getRecommend?date=${Number(date)}`
    ).then(response => {
      return Promise.resolve(response.data)
    }).catch(err => {
      console.log(err)
    })
}

当使用axios成功请求/api/getRecommend时,axios返回一个Promise对象,因为getRecommend()是要export出去的,这里直接返回一个状态完成的Promise,调用getRecommend()时,如果成功响应直接可以recommend.getRecommend().then(res => {})获取响应结果。

Promise.all()、Promise.race()并行执行多个Promise对象

  • Promise.all()是所有Promise对象状态都是‘已成功’才结束
  • Promise.race()是有一个Promise对象状态‘已成功’就结束

Promise串行执行

Promise链式调用

下面这个累加例子很好地显示了Promise之间传值计算:

// 第一步,执行XXX操作,保证了***结果
let step1 = function () {
  let val = 1
  console.log(`this is step1 , the value is ${val}`)
  // 拒绝的情况,balalala...
  if (val > 0){
    return Promise.resolve(val)
  }
  return Promise.reject(val)
}
// 第二步,执行XXX操作,保证了***结果
let step2 = function (val) {
  val += 1
  console.log(`this is step2 , the value is ${val}`)
  return Promise.resolve(val)
}
// 第三步,执行XXX操作,保证了***结果
let step3 = function (val) {
  val += 1
  console.log(`this is step3 , the value is ${val}`)
  return Promise.resolve(val)
}

step1().then((val)=>{
  return step2(val)
}).then((val)=>{
  return step3(val)
}).then((val)=>{
  console.log(`finally, the value is ${val}`)
})

// 输出结果
this is step1 , the value is 1
this is step2 , the value is 2
this is step3 , the value is 3

aysnc/await

再用async/await语法糖实现一遍累加

// 在这里我们把每一步操作看作异步的
function step21() {
  let val = 1
  console.log(`this is step1 , the value is ${val}`)
  return val
}

function step22(val) {
  val += 1
  console.log(`this is step2 , the value is ${val}`)
  return val
}

function step23(val) {
  val += 1
  console.log(`this is step3 , the value is ${val}`)
  return val
}

(async () => {
  // await使用必须在async函数内
  let val = await step21()
  val = await step22(val)
  val = await step23(val)
})()

// 输出结果
this is step1 , the value is 1
this is step2 , the value is 2
this is step3 , the value is 3

Promise数组串行

使用数组的reduce()方法依次执行,

// 第一步,执行XXX操作,保证了***结果
let step1 = function () {
  let val = 1
  console.log(`this is step1 , the value is ${val}`)
  // 拒绝的情况,balalala...
  if (val > 0){
    return Promise.resolve(val)
  }
  return Promise.reject(val)
}
// 第二步,执行XXX操作,保证了***结果
let step2 = function (val) {
  val += 1
  console.log(`this is step2 , the value is ${val}`)
  return Promise.resolve(val)
}
// 第三步,执行XXX操作,保证了***结果
let step3 = function (val) {
  val += 1
  console.log(`this is step3 , the value is ${val}`)
  return Promise.resolve(val)
}

let steps = [step1, step2, step3]
steps.reduce((promise, fn, index)=>{
  console.log(index)
  return promise.then((val)=>{
    return fn(val)
  })
  // reduce函数init参数
}, Promise.resolve())

// 输出结果
this is step1 , the value is 1
this is step2 , the value is 2
this is step3 , the value is 3

async/await数组串行

暂时只想到数组的方法,使用Array.prptotype.reduce()为解决,还是没有深入了解reduce()是如何实现的

async function foo () {
  let val
  for (let i = 0; i < steps.length; i++) {
    if (i===0) {
      val = await steps[i]()
    }else {
      val = await steps[i](val)
    }
  }
}
foo()

作者:xunuo0x
链接:https://juejin.im/post/5ab07e1e51882555731bf43b
来源:掘金


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流