回调函数、promise、async和await

88 阅读5分钟

回调函数 callback

  • 把函数A通过参数的形式传递给函数B,在函数B内部以形参的方式调用
  • 函数A就叫函数B的回调函数
  • 通常用到回到函数的场景都是在异步代码封装
  • // 1. 回调函数
           function A () {
               console.log('函数A执行')
           }
           function B(cb) {
               console.log('函数B执行')
               cb()
           }
           B(A)
    
  • 封装一个异步函数 一般都是网络请求
  • /**
           *  3. 因为网络请求, 可能会成功, 也有可能失败
           *          约定: 时间超过 3500 算作失败, 否则算成功
          */
          function fn (chenggong, shibai) {
              console.log('班长帮我去买一瓶水')
              const timer = Math.ceil(Math.random() * 3000) + 2000
              setTimeout(() => {
                  if (timer > 3500) {
                      console.log('班长买水失败', timer)
      
                      shibai()
                  } else {
                      console.log('班长买水成功', timer)
      
                      chenggong()
                  }
              }, timer)
          }
    
      fn(
          () => {
              console.log('谢谢班长辛苦了, 帮我退掉吧')
          },
          () => {
              console.log('辛苦班长了, 买不到别回来了')
          }
      )
​
  
​
# 回调地狱
​
- 回调地狱并不是一个bug,而是一种代码格式,这种代码格式,不利于我们阅读
​
- 解决Promise(期约) 他是解决回调函数代码的
​
- ```javascript
   function fn(chenggong, shibai) {
              console.log('班长帮我去买一瓶水')
              const timer = Math.ceil(Math.random() * 3000)
              setTimeout(() => {
                  if (timer > 3500) {
                      console.log('班长买水失败', timer)
  
                      shibai()
                  } else {
                      console.log('班长买水成功', timer)
  
                      chenggong()
                  }
              }, timer)
          }
          /**
           *  需求:
           *      1. 第一次买水成功以后, 再次执行一边, 但, 必须是第一次买水成功以后 才能再去买一瓶
           * 
           *      2. 在第二次买水成功以后, 再次执行一边, 但, 必须是第二次买水成功以后 才能再去买一瓶
          */
  
          fn(
              () => {
                  console.log('谢谢班长辛苦了, 帮我退掉吧')
                  fn(
                      () => {
                          console.log('第二次买水成功')
                          fn(
                              () => {
                                  console.log('第三次买水成功')
                              },
                              () => {
                                  console.log('第三次买水失败')
                              }
                          )
                      },
                      () => {
                          console.log('第二次买水失败')
                      }
                  )
              },
              () => {
                  console.log('辛苦班长了, 买不到别回来了')
              }
          )
  

认识 Promise

  • 一个新的异步代码封装方案 以前通过回调函数的形式去封装,会导致出现回调地狱 所以用Promise来解决
  • promise有三个状态:
  • 持续:pending
  • 成功:fulfilled
  • 失败:rejected
  • promise只会发生两个转换
  • 持续===>成功
  • 持续===>失败
  • promise实例化对象上有两个方法
  • .then-----触发成功状态
  • .catch----触发失败状态
  • promise实例化对象可以接受链式调用
  • const p = new Promise(function (reslove, reject) {
               /**
                *  resolve 会把我们这个 promise 状态转换为 成功
                *  reject 会把我们这个 promise 状态转换为 失败
                * 
                *      这两个 都是 函数
               */
               // 写上我们的异步代码
      
               // console.log('班长帮我去买一瓶水')
               const timer = Math.ceil(Math.random() * 3000) + 2000
               setTimeout(() => {
                   if (timer > 3500) {
                       console.log('班长买水失败', timer)
      
                       reject('班长买水失败, 再换个地方买')
                   } else {
                       console.log('班长买水成功', timer)
      
                       reslove('班长买水成功, 奖励二十个bug')
                   }
               }, timer)
      
           })
      
           // 变量p 就是 promise 的实例化对象
           // console.log(p)
    
      /**
       *  promise 实例化对象 上有两个方法
       *      .then----触发成功状态
       *      .catch-----触发失败状态
      */
  
      // p.then(function (res) {
      //     /**
      //      *  再 promise 成功状态时触发
      //      *      并且接受 resolve时传递的参数(请看50行代码)
      //     */
      //     console.log('0000000000', res)
      // })
      // p.catch(function (res) {
      //     /**
      //      *  再 promise 失败状态时触发
      //      *      并且接受 reject 时传递的参数(请看46行代码)
      //     */
      //    console.log('11111111', res)
      // })
  
      /**
       *      promise 实例化对象 可以接受 链式调用
      */
      p.then(function (res) {
          console.log('0000000000', res)
      }).catch(function (res) {
          console.log('11111111', res)
      })
​
​
​
# 封装Promise
​
```javascript
 function fn() {
            const p = new Promise(function (reslove, reject) {
                const timer = Math.ceil(Math.random() * 3000) + 2000
                setTimeout(() => {
                    if (timer > 3500) {
                        reject('班长买水失败, 再换个地方买')
                    } else {
                        reslove('班长买水成功, 奖励二十个bug')
                    }
                }, timer)
​
            })
            return p
        }
​
​
        /**
         *  再promise.then 内部 return 了一个 新的 promise对象
         *      可以在后续再写一个 .then
        */
        fn().then((res) => {
            console.log('班长第一次买水成功')
            return fn()
        }).then((res) => {
            console.log('班长第二次买水成功')
            return fn()
        }).then((res) => {
            console.log('班长第三次买水成功')
        }).catch((res) => {
            console.log('班长买水失败')
        })

async 和 await

  • 能帮助我们把异步代码,写的和同步代码一样
  • 函数开头必须写async表面内部可以书写await
  • function fn() {
              const p = new Promise(function (reslove, reject) {
                  const timer = Math.ceil(Math.random() * 3000)
                  setTimeout(() => {
                      if (timer > 3500) {
                          reject('班长真好, 就是买水失败, 再换个地方买')
                      } else {
                          reslove('班长真好, 就是买水成功, 奖励二十个bug')
                      }
                  }, timer)
      
              })
              return p
          }
          // 1. 函数开头必须书写 async 表明内部可以书写 await
          async function newFn() {
              /**
               *  await 后边需要跟着 promise
               *      await 表示等到的意思, 执行到 fn()   虽然是异步的
               *      但是因为有 await 关键字, 此时不会往下继续执行, 
               *      而是等待 fn() 执行完毕, 在往下执行
              */
              let r1 = await fn()
              console.log(r1)
              // console.log('如果失败了, 执行这行代码, 提示用户网络有问题')
          }
          newFn()
          // promise 常规写法
          // fn().then((res) => {
          //     console.log('班长第一次买水成功')
          // })
    

async 和 await 语法的缺点

  • m没有办法捕获到错误,只能接受promise的成功状态 如果报错,会中断程序执行

  • 解决方法1:try.....catch

  • function fn() {
                const p = new Promise(function (reslove, reject) {
                    const timer = Math.ceil(Math.random() * 3000) + 4000
                    setTimeout(() => {
                        if (timer > 3500) {
                            reject('班长真好, 就是买水失败, 再换个地方买')
                        } else {
                            reslove('班长真好, 就是买水成功, 奖励二十个bug')
                        }
                    }, timer)
    ​
                })
                return p
            }
            
         /**
             *  解决方法1: try...catch
             * 
             *          首次执行的时候 会走 try 这个分支, 如果这个位置有报错
             *          他会结束执行 try 分支, 然后走 catch 分支
             * 
             *          如果再运行 try 分支的时候, 没有报错, 那么 catch 不会运行
            */
            async function newFn() {
                try {
                    let r1 = await fn()
                    console.log(r1)
                } catch (error) {
                    console.log(error)
                    console.log('如果失败了, 执行这行代码, 提示用户网络有问题')
                }
            }
            newFn()
        
    
  • 解决方法2:更改promise的封装

  • /**
           *  解决方法2: 更改 promise 的封装
           * 
           *      原因: promise 执行 reject 时 async await 不能捕获到错误,
           * 
           *      解决: 让这个 promise 不管什么情况 都返回 resolve
           *              我们通过 返回的 参数, 区分现在时成功还是失败
           * 
           * 
           *  开发中 对象内的 code 如果为0, 一般代表失败
           *          对象内的 code 如果为1, 一般代表成功
          */
          function fn() {
              const p = new Promise(function (reslove, reject) {
                  const timer = Math.ceil(Math.random() * 3000) + 1000
                  setTimeout(() => {
                      if (timer > 3500) {
                          // reject('班长真好, 就是买水失败, 再换个地方买')
                          reslove({
                              code: 0,
                              msg: '班长真好, 就是买水失败, 再换个地方买'
                          })
                      } else {
                          reslove({
                              code: 1,
                              msg: '班长真好, 就是买水成功, 奖励二十个bug'
                          })
                      }
                  }, timer)
      
              })
              return p
          }
          async function newFn() {
              let r1 = await fn()
              if (r1.code === 0) {
                  console.log('您的网络有问题')
              } else {
                  console.log(r1.msg)
              }
          }
          newFn()