1. 首页
  2. 前端基础

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

对写文章这件事已经阁了3个月了,工作太忙很难抽出大块时间来总结写作。现在稍微闲赋些,准备好好对以前的技术做一下总结。为什么要写关于 Promise 呢?有以下 3 点

  1. 之前面试时候,面试官问 Promise 问的比较多,实在要对它进行一个好好总结了,也是 JavaScript 比较难懂的一个技术点,平时我面试别人时候,也喜欢问 Promise 相关的,保证他在工作中能够熟练运用,自己能封装axios,能控制比较复杂的同步异步流程等…

  2. 这三个月的工作中,自己也总是遇到关于 Promise 的一些运用场景,有时候又比较疑惑一些地方,也是为自己总结一下 Promise,以后在工作中更加运用自如。

    接下来我从最基础的同步异步谈起,再通过图示谈他的语法,如何自己写一个Promise,再谈一下我在工作中遇到的 Promise,最后谈一下 新的语法 asyncawaitPromise 的结合和比较

  3. 网上关于Promise的介绍大多数是一些语法的介绍,大多没有结合场景和同步异步相关理解来谈。对于理解Promise的前世今生还是不够深入,不够具体。

理解同步异步

理解JS执行原理

console.log(1)
setTimeout(() => {
    console.log(2)
}, 1000)
console.log(3)

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 如果延迟时间为0

console.log(1)
setTimeout(() => {
    console.log(2)
}, 0)
console.log(3)

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

console.log(1)
setTimeout(() => {
    console.log(2)
}, 2000)
console.log(3)
setTimeout(() => {
    console.log(4)
}, 1000)
console.log(5)
setTimeout(() => {
    console.log(6)
}, 0)
console.log(7)
setTimeout(() => {
    console.log(8)
}, 1000)
console.log(9)

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

这里可以发现几点

  • 1,3,5,7,9 是同步代码,6,4,8,2是异步代码,同步先于异步执行
  • 单看同步代码,按位置顺序依次执行
  • 单看异步代码,不同执行时间,越长越靠后执行。相同执行时间,按位置顺序依次执行

这里我们总结下同步与异步的规律

  • 代码中存在异步函数,不管需要时间多久,都要在同步完成后执行
  • 同步按位置顺序执行;异步按时间长短执行,同样时间长短时,按位置顺序执行

这是一个怎样的机制,JS引擎又是如何处理的呢?

我找到了一张这样的图

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

首先来解释一下这个图:这是JS的事件循环,分为3个步骤

  1. 引擎将同步、异步函数按次序载入到执行栈里面
  2. 执行栈的将异步函数放入异步线程里面
  3. 线程根据任务完成时间依次推入任务队列中执行

如何处理异步呢, 最初的方法是使用回调

理解回调

引入一下知乎上的高赞回答 如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 现在我们把它用代码写出来看看

function fetchSomething() {
  console.log('去取货!')
}

function buySomething(callback) {
  console.log('没货了!')
  setTimeout(() => {
    console.log('有货了!')
    callback()
  }, 1000)
}

buySomething(fetchSomething)

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 用简单的语言描述就是:将一个函数作为参数传给另一个函数调用

要搞清楚的一点是,回调和异步没有直接的关系,也可以同步回调,也可以异步回调。我们是通过回调这个机制来实现异步的操作,例如

// 这个一个请求的异步函数,来实现异步操作
function getSomePeopleName(params, callback) {
  setTimeout(() => {
    let data

    if (params === 'suo') {
      data = 'yue'
    }

    console.log(callback(data))
  }, 1000)
}
console.log(getSomePeopleName('suo', (data) => '我是回调函数:' + data))

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 回调函数的结果一定是在回调函数里面,如果我是这样一个流程呢?

A -> B -> C -> D

function A(callback) {
  console.log('开始执行A')
  setTimeout(() => {
      callback('A')
  }, 500)
}

function B(callback) {
  console.log('开始执行B')
  setTimeout(() => {
      callback('B')
  }, 400)
}

function C(callback) {
  console.log('开始执行C')
  callback('C')
}

function D(callback) {
  console.log('开始执行D')
  setTimeout(() => {
      callback('D')
  }, 200)
}

A((a) => {
  B((b) => {
    C((c) => {
      D((d) => {
        console.log(a, b, c, d)
      })
    })
  })
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 由上可知,回调函数有几个特定

  • 使用回调函数嵌套,回调函数一定在上一个回调之后执行,用于可以控制流程,不会出现异步函数在执行顺序的混乱,即使同步异步函数混合
  • 回调函数解构嵌套,代码不够清晰,俗称回调地狱

那么有没有更好的异步操作机制呢?这里我们就要谈到 Promise

深入 Promise

为什么要有Promise?

ES6 标准中 Promise 语法

  • Promise是一个构造函数

我们写一个简单的 Promise 看看

new Promise(() => {})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

// 有一个参数executor的构造函数
// executor 也是一个函数,具有两个参数 resolve, reject 是两个回调函数,当执行到回调函数时,会执行
const isResolve = true
new Promise((resolve, reject) => {
    if (isResolve) {
        resolve()
    } else {
        reject()
    }
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 可以看到 Promise 的状态从pending变成resolved

如果将 isResolve 置为 false 呢?

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) Promise 状态从 pending 变成 rejected 同时抛出一个异常,并且异常未被捕获,所以我们写Promise时候一定要加上catch 来捕获异常

const isResolve = false
new Promise((resolve, reject) => {
    if (isResolve) {
        resolve()
    } else {
        reject('我拒绝你')
    }
}).catch((err) => {
  console.log('捕获异常', err)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 现在我们把异常捕获到了,但是神奇的事情发生了,异常状态应该是rejected,怎么变成 resolved了呢? 我们或许很纳闷,这个放在后面讨论,我们先看看 then 怎么处理的

const isResolve = true
new Promise((resolve, reject) => {
    if (isResolve) {
        resolve('通过')
    } else {
        reject('我拒绝你')
    }
}).then((res) => {
  console.log('resolve', res)
}, (res) => {
  console.log('reject', res)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)isResolve = false

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 我们可以看到 then 是怎么处理的

  1. 如果 前面resolve()调用,Promise状态为 resolvedthen则执行第一个回调函数参数
  2. 如果 前面reject()调用,Promise状态为 rejectedthen则执行第二个回调函数参数
  3. then 执行回调函数之后Promise 的 状态都为 resolved

下面进一步验证下

const isResolve = false
new Promise((resolve, reject) => {
    if (isResolve) {
        resolve('通过')
    } else {
        reject('我拒绝你')
    }
}).catch(err => {
  console.log('我捕获到了', err)
}).then((res) => {
  console.log('resolve', res)
}, (res) => {
  console.log('reject', res)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 果然呢,catch之后,then还会去执行,并且resolve 的参数为 undefined

问题是现在我们如何控制一个流程呢?Promise并不能直接给我们进行长流程的分支选择

下面有一个简单流程

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 尝试使用Promise去控制流程

const process = [102, 204]
new Promise((resolve, reject) => {
  if (process[0] === 101) {
     setTimeout(() => {
      console.log(101)
      resolve(101)
    }, 1000)
  } else {
    setTimeout(() => {
      console.log(102)
      reject(102)
    }, 1000)
  }
}).then(() => {
  if (process[1] === 201) {
    setTimeout(() => {
      console.log(201)
    }, 500)
  } else {
    setTimeout(() => {
      console.log(202)
    }, 500)
  }
}, () => {
    if (process[1] === 203) {
    setTimeout(() => {
      console.log(203)
    }, 500)
  } else {
    setTimeout(() => {
      console.log(204)
    }, 500)
  }
}).catch((err) => {
  console.log(err)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 下面我们试试更加复杂的流程

Promise 调用链:then 的作用

单个Promise没办法控制长流程,我们怎么将Promise形成一个控制链呢,需要理解then的返回在其中起到的作用

promise.then(onFulfilled, onRejected)

  • 接收两个参数,onFulfilledpromiseresolved 状态被调用

  • 接收两个参数,onRejectedpromiserejected 状态被调用

  • 返回值比较复杂,下面用表格列出来

    | 序号 | 场景 | 返回的Promise状态改变为 | 回调函数参数值 | | —- | —————– | ———————– | ————————- | | 1 | 返回1 个值 | resolved | 返回值 | | 2 | 没有返回 | resolved | undefined | | 3 | 抛出错误 | rejected | 错误 | | 4 | resolved的Promise | resolved | 返回的Promise回调的参数值 | | 5 | rejected的Promise | rejected | 返回的Promise回调的参数值 | | 6 | pending的Promise | pending | 返回的Promise回调的参数值 |

我们可以看出想要控制then 的后续流程,必须通过这 6 种情况来控制 下面来测试一下 这 6 种情况是否符合我们的预期

  1. 返回一个值
new Promise((resolve, reject) => {
        resolve('通过')
}).then((res) => {
  console.log('1', res)
  return res // then的返回值
}).then((res) => {
  console.log('2', res)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

  1. 不返回

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

  1. 抛出错误

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

  1. resolved的Promise

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

  1. rejected的Promise

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

  1. pending的Promise

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

看图写代码

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

分析特点:

这样就比较复杂了,还要考虑暂停的问题,但是根据我们上面测试到的,通过then的返回值控制流程也没有想象那么难

task([101, 201])
function task(testPath) {
  console.log('开始测试', testPath)
  new Promise((resolve, reject) => {
    console.log('000')
    if (101 === testPath[0]) {
      resolve('101')
    } else {
      reject('102')
    }
  }).then((res) => {
    console.log('201', '上一个返回:' + res)
    return new Promise(() => {})
  }, (res) => {
    if (202 === testPath[1]) {
      console.log('202',  '上一个返回:' + res)
      return '202'
    } else {
      console.log('203',  '上一个返回:' + res)
      throw '203'
    }
  }).then((res) => {
      console.log('301',  '上一个返回:' + res)
      return Promise.resolve('301')
  }, (res) => {
       console.log('302',  '上一个返回:' + res)
      return Promise.resolve('302')
  }).then((res) => {
    console.log('401',  '上一个返回:' + res)
    return Promise.reject('401')
  }).catch((err) => {
    console.log(err)
  })
}

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

  • Promise.resolve() 等同用 new Promise((resolve, reject) => resolve())
  • Promise.reject() 等同用 new Promise((resolve, reject) => reject())
  • Promise.all()
// 作为参数promise 数组中,所有promise状态都是resolved才回调then第一个,只要有一个reject就reject
Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]).then((res) => {
  console.log('通过', res)
}, (res) => {
  console.log('拒绝', res)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 返回值为全部值的一个数组

Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.reject(3)]).then((res) => {
  console.log('通过', res)
}, (res) => {
  console.log('拒绝', res)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) 返回值仅返回拒绝的那个

  • Promise.race()
Promise.race([new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(1)
  }, 1001)
}), new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2)
  }, 1002)
})]).then((res) => {
  console.log('通过', res)
}).catch((res) => {
  console.log('拒绝', res)
})

如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入) race 相当于竞赛,多个Promise竞赛,谁先状态变成resolvedrejected,谁就执行下面的回调(根据时间来抉择)

下节预告

Promise 到 generator 再到 async & await

Promise 实际应用

对异步请求封装

流程控制

手写一个Promise

后记

总结一下,从同步异步到回调,在到Promise的语法和应用全部都可以在谷歌浏览器的控制台中输出测试。通过一点点代码的编写和输出,才会让我们思维更清晰,对Promise的理解更深刻。之后再总结工作中用到的Promise,以后也会慢慢将asyncawait结合Promise来谈关于 es6 以后异步相关的新特性。

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:如何让 Promise 控制业务流程(Re 同步异步 开始一步一步深入)

链接:https://www.javascriptc.com/3780.html

« 这些ECMAScript 2015(ES6)有用的提示与技巧你都会了没?
目前(2019)可以使用ES10的这5个新特性你懂了没»
Flutter 中文教程资源

相关推荐

QR code