什么是回调函数?我在await一个回答

100 阅读5分钟

回调函数 callback

  • 回调函数嘛 就是在一个函数结束时去调用另外一个函数 当然是以参数传递的方式
  • 假设现在有函数a和函数b 将b函数以参数形势传入 a函数中 在a函数内部调用 b函数
    • 用一个倒计时器 让a和b的先后顺序更明显
        function a (callback) {
            console.log('我是a')
                setTimeout(() =>  callback() ,1000)
            }
        function b () {
                console.log('我是b')
            }
        a(b)//
    
  • 封装一个异步函数 (通常用于网络请求)
    • 举个栗子
        function myCallback (callback) {
            let timer = Math.ceil(Math.random() * 3000)
                setTimeout(() => {
                        console.log('异步代码开始执行')
                        callback()
                    },timer)
            }
        function fn () {
                console.log('异步代码执行结束 我才执行')
            }
        myCallback(fn)
    
  • 实际上网络请求有成功 肯定也有失败 所以上个栗子 也就是个栗子
    • 栗子思路:
      • 首先我们创建一个结束时间随机的倒计时器
      • 我们规定 当结束时间大于1500ms为请求失败
      • 小于1500ms即为请求成功
      • 函数接收两个参数success为成功是调用 fail为失败时调用
            function myCallback (success, fail) {
                let timer = Math.ceil(Math.random() * 3000)
                    setTimeout(() => {
                           if (timer > 1500) {
                                    console.log('网络连接失败啦')
                                    fail()
                                } else {
                                    console.log('请求成功')
                                    success()
                                }
                        },timer)
                }
                myCallback(
                    () => console.log('数据已帮您更新'),
                    () => console.log('请检查您的网络')
                )
    

回调地狱

  • 回调地狱并不是bug 而是一种不利于阅读的代码格式
    • 借用上一个栗子
    • 第一次请求成功后继续请求...n次后效果..
            function myCallback (success, fail) {
                let timer = Math.ceil(Math.random() * 3000)
                    setTimeout(() => {
                           if (timer > 1500) {
                                    console.log('网络连接失败啦')
                                    fail()
                                } else {
                                    console.log('请求成功')
                                    success()
                                }
                        },timer)
                }
                myCallback(
                    () => {
                            console.log('数据已帮您更新')
                            myCallback(
                                    () => {
                                        console.log('数据已帮您更新')
                                        myCallback(
                                                () => console.log('数据已帮您更新'),
                                                () => console.log('请检查您的网络')
                                            )
                                    },
                                    () => console.log('请检查您的网络')
                                )
                            },
                    () => console.log('请检查您的网络')
                )
    
  • 当然肯定有解决方案
    • Promise()
    • async和await

认识 Promise

  • Promise是Es6新推出的方案 在es5及以前的版本中 开发者会书写回调地狱
  • ES6内置构造函数
  • Promise就是专门用于解决回调地狱的问题
  • promise的三个状态
    • pending(持续)
    • fulfilled(成功)
    • rejected(失败)
    • Promise的状态转换
      • pending(持续) ==> rejected(失败)
      • pending(持续) ==> fulfilled(成功)
      • 失败(rejected)<=!=>成功(fulfilled) 之间不会相互转换
  • 语法:
            let pro = new Promise(() => {
                //需要或者想要执行的异步代码
            })
    
    • Promise为内置构造函数 其实例化对象可以使用两个方法
      • pro.then(() => {}) 成功状态时执行
      • pro.catch(() => {}) 失败状态时执行
        • catch会捕获到reject失败报错信息
        • 如果失败报错不被捕获的话 后续代码将无法执行
      • 两者不会同时执行
  • then 和 catch 链式调用
    • 可以多次书写then
    - Pro.then(res => {
        console.log(res)
    }).then(res => {
        console.log(res)
    })....
    .catch( res => {
        console.log('失败了')
    })

async 和 await

  • 和Promise 配合
  • 可以让异步代码做到和同步代码一样的效果(类似)
    • async关键字 必须书写在函数名前面
      • 表示在函数内部可以使用 **await**关键字
    • await关键字的含义
      • 需要等待await后面的Promise对象执行完成之后才会继续执行后续代码

async 和 await 语法的缺点

  • await无法捕获到promise的失败状态 也就是说在promise状态为失败的时候 后续代码将无法执行
  • 解决方案1
    • 手动修改Promise的值(状态) 让其永远都是成功状态、
    • 然后可以让其返回一个数组 或者对象 对象或者数组内存放一个标志值
    • 后续我们只需要判断其的数值的类型
  • 解决方案2
    • 使用 try...catch方法
    • 原理:try...catch方法首先会执行try内的代码
      • 当代码报错 会执行catch内的代码 catch内代码在try内代码没有报错的情况下不会被执行
              async function myPromise () {
                  try {
                      let s = await fn()
                  } catch (Error) {
                      console.log('try报错后 我才执行')
                  }
              }
      

Promise 的其他方法

  • Promise()的实例化对象上还有一个方法
  • finally 方法会在Promise执行结束之后才会执行(状态转换完成之后)
  • 无论状态是成功还是失败 都会执行
    - finally(res => {})
    

Promise实例化对象上的其他方法

  • all 方法
    • 该方法接收一个数组
    • 数组内可以传递多个Promise实例化对象
    • 类似数中的every()方法
    • 当每一个传入对象的状态都为成功时 all方法会成功
    • 但凡有一个对象失败 all() 方法都会失败
  • race方法
    • 接收一个数组
    • 数组内接收多个Promise的实例化对象
    • 该方法类似数组的some方法
      • 都是对象中有一个成功 状态就为成功
      • 区别
        • some()方法是针对所有项
        • race()方法只针对第一项
      • 当传入的对象中的第一项的结果为成功 race()方法成功
      • 否则race() 方法为失败
  • allSettled()
    • 接收一个数组
    • 数组内可以传入多个Promise的实例化对象
    • allSettled()方法内部只会执行then()
    • 会将每一个对象的状态储存在数组中

数组扁平化

  • 数组扁平化 类似深拷贝
    • 深拷贝是针对引用数据
    • 数组扁平化只是针对数组
  • 数组扁平化 意指将多维数组装换为一维数组
    • 思路:
      • 首先构造一个函数
      • 然后遍历数组 顺便对每一项做数据类型检测
        • 检测方法
        • Object.prototype.toString.call(item)
        • constructor === Array
        • item instance Array
      • 基本数据类型直接添加到新数组内
      • 数组内的数组 继续调用分解函数(递归)
      • 直到数组内没有数组
    let arr = [1, 2, 3, [4, 5, [6, 7, [8, 9, [10, 11]]]]]
    function myFlat (origin) {
        let newArr = []
        function inner(innerOrigin) {
            innerOrigin.forEach(item => {
                if (Object.prototype.toString.call(item) === '[object Array]'){
                    inner(item)
                } else {
                    newArr.push(item)
                }
            })
        }
        inner(origin)
        return newArr
    }