[js] 回调函数 回调地狱 Promise async/await

2022年8月31日12:15:31

1. 回调函数 callback

  • 一种封装代码的手段

  • 什么是 callback , 概念

    => 把 函数A 当做 实参 传递到 函数B 内部

    => 在 函数B 内部以 形参 的方式 调用 函数A

    => 我们管这个行为叫做 回调函数

    => 我们说 函数A 是 函数B 的 回调函数

functionA(){
  console.log('我是 A 函数')}functionB(fn){// 此时 fn 形参接受的是书写在 B() 的时候, () 内部的内容 : A// 此时 fn 形参接受的就是全局 函数 A 的地址// 此时 fn 形参和全局变量 A 操作一个函数空间
  console.log('我是 B 函数')// 调用 fn 其实就是在调用执行全局的 A 函数fn()}// 调用 B 函数// A 是一个保存 函数的地址// 把 函数 A 这个地址当做实参传递给了 B 函数内部的 fn 形参B(A)// 函数A 是 函数B 的回调函数
  • 为什么需要 callback 回调函数

  • 如果从头到尾都是 同步代码, 不需要回调函数

    => 当你在 封装代码 的时候

    => 并且代码内有 异步 的时候

    => 并且需要在 异步的 末尾 做一些事情的时候

    => 使用 callback

解释: 为什么异步的末尾封装要使用 callback

  • 因为 JS 的单线程,同一个时间点只能做一个事情

  • 主要: 异步的结束时间不确定

  • 例子: 外卖

    => 一个外卖员同一个时间点只能做一件事情

    => 如果你希望多带一双筷子

    => 方案1: 等到外卖员刚好到达店里的时候, 给他打电话

    => 方案2: 在点餐的时候给一个备注


回调函数的缺点:

  • 回调地狱

  • 当回调 嵌套 回调的时候, 代码的阅读和可维护性不高

解决回调地狱的问题:

  • Promise 来解决回调地狱

  • 分析:

    => Promise 是来解决回调地狱

    => 回调地狱, 是因为回调函数嵌套过多

    => 回调函数, 为了解决在异步末尾做一些事情的封装

    => Promise 就是一种优雅的对于异步代码封装的方案

// 为什么需要回调函数// 封装一段代码// 例子 : 外卖公司做好的事情functionwaimai(beizhu){// 获取一个 1000 ~ 6000 的随机整数const time=1000* Math.round(Math.random()*5+1)
  console.log(' 在路上 '+ time)// 我们使用 setTimeout 模拟一个网络环境请求setTimeout(()=>{
    console.log('到达店里了, 拿到外卖')// 直接把我需要执行的代码放在这个位置// 那么这个封装就没有意义了// 就需要用到回调函数了// 因为这个位置是异步的末尾了// 这个位置调用 beizhu 就是在异步的末尾调用// 例 : 不管什么时候到了店里// 拿到外卖以后, 把 备注 的内容执行一下beizhu()}, time)}// 用户的需求: 想多拿一双筷子waimai(function(){ console.log('多拿一双筷子')})// 用户的需求: 想多拿点辣椒waimai(function(){ console.log('多拿点辣椒')})

[js] 回调函数 回调地狱 Promise async/await

2. 回调地狱

理解

  • 一种使用回调函数封装的代码时候的情况

  • 回调函数的使用是有 函数嵌套 在里面的

  • 当你大量使用回调函数封装的代码的时候, 会出现 结构紊乱

    => 不利于代码的阅读和维护

  • 为了解决回调地狱

    => ES6 的语法内出现了一个新的语法, 叫做 Promise

    => 为了把 异步代码 封装变成 Promise 语法的封装

    => 不再使用 回调函数 来封装 异步代码了

    => 本质: 用来 封装异步代码 的

实现需求 :

  1. 发送一个请求, 请求一个接口

    => 等到响应回来以后

    => 把内容打印在控制台

  2. 发送第二个请求, 请求第二个接口

    => 要求必须要在第一个请求结束以后, 打印完毕以后再次发送请求

    => 把响应内容打印在控制台

  3. 发送第三个请求, 请求第三个接口

    => 要求, 必须要在第二个请求结束以后, 打印完毕以后再次发送请求

    => 把响应内容打印在控制台

<script src="jquery.min.js"></script>// 实现需求 1 :
$.ajax({
  url:'http://localhost:8888/test/first',success:function(res){
    console.log('第一次请求的结果')
    console.log(res)// 这个位置的代码执行的时候, 一定是第一个请求结束的时候// 需求2:
    $.ajax({
      url:'http://localhost:8888/test/second',
      dataType:'json',success:function(res){
        console.log('第二次请求的结果')
        console.log(res)// 这个位置的代码执行的时候, 一定是第二个请求结束的时候// 需求3:
        $.ajax({
          url:'http://localhost:8888/test/third',
          data:'name=Jack&age=18',
          dataType:'json',success:function(res){
            console.log('第三次请求的结果')
            console.log(res)}})}})}})

这里的ajax用的是jquery

[js] 回调函数 回调地狱 Promise async/await

3. 认识 Promise

  • 是一个 ES6 出现的语法

  • Promise 也是一个 JS 内置的 构造函数

  • promise - 承诺 :

    • 承诺的状态有多少个 ?
      => 继续(持续执行过程中)
      => 成功
      => 失败
    • 承诺状态之间的转换 : 只能转换一次
      => 要么是 继续 转换成 成功
      => 要么是 继续 转换成 失败
  • Promise 也有三个状态
    => 继续: pending
    => 成功: fulfilled
    => 失败: rejected

const p=newPromise(function(resolve, reject){// ...// ...})// 给当前这个承诺注册一个 成功以后的函数
p.then(function(){})// 给当前这个承诺注册一个 失败以后的函数
p.catch(function(){})

如何改变 promise 的状态

  • 在 new Promise 的 a 函数内
  • 可以接受两个参数
    1.第一个参数: 可以将该 Promise 的状态由继续转换为 成功
    2.第二个参数: 可以将该 Promise 的状态由继续转换为 失败
// 1. 异步代码const p=newPromise(function(resolve, reject){// resolve 就是一个转换成功的方法// 当你书写 resolve() 的时候, 就是在把 该 promise 的状态转换为成功// 就会执行 .then 时候里面书写的 b 函数// reject 就是一个转换成失败的方法// 当你书写 reject() 的时候, 就是在把 该 promise 的状态转换为失败// 就会执行 .catch 时候里面书写的 c 函数// 这两个只能书写一个// 书写你需要封装的异步代码const time=1000* Math.round(Math.random()*5+1)
  console.log('承诺一辈子在一起')setTimeout(()=>{if(time>=3000){// resolve() 调用的是 then 内部的函数 b// 所以这里书写在 () 内部的 time 内容就是给到 then 内 b 的实参resolve(time)}else{// reject() 调用的是 catch 内部的函数 c// 所以这里书写在 () 内部的 time 内容就是给到 catch 内 c 的实参, 也是报错信息reject(time)}}, time)})// promise 对象调用的两个方法// 注册 成功
p.then(functionb(t){// 函数 b 不会被直接调用的// 这个位置的代码会在 p 这个 promise 的状态由 继续 转换为 成功 的时候调用执行
  console.log(t,'成功的函数 b')// t 就是你在 promise 内部书写的 resolve 的小括号里面 time 的内容})// 注册 失败
p.catch(functionc(err){// 函数 c 不会被直接调用// 这个位置的代码会在 p 这个 promise 的状态由 继续 转换为 失败 的时候调用执行
  console.log(err,'失败的函数 c')})

[js] 回调函数 回调地狱 Promise async/await

4. Promise 的进阶语法

  • 当一个 Promise 的 then 内的代码

  • 只要你在前一个 then 内部以 return 返回一个新的 promise 对象 的时候

  • 新 promise 对象的 then 可以直接在前一个 then 的后面继续书写 then

需求:

  1. 发送一个请求, 请求第一个接口

  2. 发送第二个请求, 请求第二个接口
    => 前提: 必须要等到第一个请求结束以后再次发送

functionmyPromiseAjax(options={}){const p=newPromise((resolve, reject)=>{// 执行 ajax
    $.ajax({
      url: options.url,
      data: options.data,
      type:options.type,
      dataType: options.dataType,success:function(res){resolve(res)}})})// 把我的 promise 对象返回出去return p}
myPromiseAjax({
    url:'http://localhost:8888/test/first'}).then(res=>{
    console.log('第一个请求结束了')
    console.log(res)// return 一个新的 promise 对象returnmyPromiseAjax({
      url:'http://localhost:8888/test/second',
      dataType:'json'})}).then(res=>{
    console.log('第二个请求结果')
    console.log(res)// return 一个新的 promise 对象returnmyPromiseAjax({
      url:'http://localhost:8888/test/third',
      data:'name=Jack&age=20',
      dataType:'json'})}).then(res=>{
    console.log('第三次请求的结果')
    console.log(res)})

[js] 回调函数 回调地狱 Promise async/await

5. async 函数 和 await 关键字

  • ES7 ~ ES8 之间出现的语法

  • 作用 :

  • 为了解决 Promise 的问题 , 把 Promise 的代码书写的更优雅

  • 核心作用: 把 异步代码 写的 看起来像 同步代码, 本质还是异步

语法:

=> async 关键字 (异步)

  • 使用: 书写在函数的前面

(可以是声明式函数, 可以是函数表达式, 可以是箭头函数)

// async 的语法asyncfunctionfn(){}constfn=asyncfunction(){}constfn=asynca=>{}

作用:

  1. 该函数内可以使用 await 关键字了

  2. 会把该函数变成一个 异步函数, 只是叫做 异步函数

    (这个异步函数并不是我们真实的异步代码,只是给这个函数起了个名字)

    => 影响的是函数内部的代码 , 不影响函数外面的代码

await 关键字 (等待)

  • 要求:
  1. await 必须写在一个有 async 关键字的异步函数内部

  2. await 后面等待的内容必须是一个 promise 对象 , 否则等不了

  • 作用:

    => 把 promise 中本该在 then 内代码接受的结果 ,

    可以直接在 await 前面定义变量接受

    => 后续的代码需要等到 promise 执行完毕才会执行

functionmyPromiseAjax(options={}){const p=newPromise((resolve, reject)=>{// 执行 ajax
    $.ajax({
      url: options.url,
      data: options.data,
      type:options.type,
      dataType: options.dataType,success:function(res){resolve(res)}})})// 把我的 promise 对象返回出去return p}
console.log('start')// ① startasyncfunctionfn(){
  console.log('我是 fn 函数内部的代码')// ②// 因为 myPromiseAjax是按照 promise 的语法形式进行封装的代码// myPromiseAjax会返回一个 promise 对象// fn 函数内, 执行到 myPromiseAjax这个代码的时候// 会等待, 等到这个异步的代码完全执行完毕, 把结果赋值给 r1 以后// 在继续执行后面的代码const r1=awaitmyPromiseAjax({ url:'http://localhost:8888/test/first'})
  console.log(r1)// ④}fn()
console.log('end')// ③ end

[js] 回调函数 回调地狱 Promise async/await

console.log('start')asyncfunctionfn(){
  console.log('我是 fn 函数内部的代码')// 此时 fn 函数内可以使用 await 关键字了// myPromiseAjax返回出来的 promise 对象会执行// 把 resolve() 的时候 括号里面的内容 赋值给 r1. 在继续向后执行代码const r1=awaitmyPromiseAjax({ url:'http://localhost:8888/test/first'})
  console.log(r1)// // 需求2:const r2=awaitmyPromiseAjax({
    url:'http://localhost:8888/test/second',
    dataType:'json'})
  console.log(r2)// 需求3:const r3=awaitmyPromiseAjax({
    url:'http://localhost:8888/test/third',
    data:'name=Jack&age=20',
    dataType:'json'})
  console.log(r3)}fn()

console.log('end')

[js] 回调函数 回调地狱 Promise async/await

  • 作者:533_
  • 原文链接:https://blog.csdn.net/qq_14993591/article/details/120531881
    更新时间:2022年8月31日12:15:31 ,共 5556 字。