前端 JavaScript 之 Promise _ 前后版

news/2024/7/10 23:01:55 标签: 前端, javascript, es6

目录

1. 回调函数 callback

2. 回调地狱

3. 认识 Promise

4. Promise 的进阶语法

5. async 函数 和 await 关键字

Promise 简图 :


1. 回调函数 callback

  + 一种封装代码的手段

  + 什么是 callback , 概念

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

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

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

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

javascript">function A() {
  console.log('我是 A 函数')
}

function B(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 就是一种优雅的对于异步代码封装的方案

javascript">// 为什么需要回调函数
// 封装一段代码
// 例子 : 外卖公司做好的事情
function waimai(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('多拿点辣椒') })

 


2. 回调地狱

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

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

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

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

  + 为了解决回调地狱

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

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

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

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

 实现需求 :

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

  => 等到响应回来以后

  => 把内容打印在控制台

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

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

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

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

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

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

+ 问题1: 在什么位置发送第二个请求 ?

=> ajax 是同步还是异步, 异步的

=> 打开页面, 会马上把第一个请求发送出去

=> 第一个请求还没有回来的时候, 继续发送了第二个请求

=> 因为第一个请求的 success 一定会在请求结束后才执行

=> 我需要把第二个请求的代码放在第一个请求的 success 内部

问题: 代码的阅读和可维护性不高

+ 代码嵌套过多

javascript">// 实现需求 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)
          }
        })
      }
    })
  }
})

3. 认识 Promise

+ 是一个 ES6 出现的语法

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

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

javascript">const p = new Promise(function (resolve, reject) {
  // ...
  // ...
})
p.then(function () {
  // ...
})
p.catch(function () {
  // ...
})

Promise 的基础语法 :

  => const p = new Promise(function a( ) {

    // 你要封装的异步代码

  })

  => promise 对象可以调用两个方法

    1. p.then(function ( ) { })

    2. p.catch(function ( ) { })

  + promise对象.then(function ( ) { ... })

    => 给当前这个承诺注册一个 成功以后的函数

  + promise对象.catch(function ( ) { ... })

    => 给当前这个承诺注册一个 失败以后的函数

  如何改变 promise 的状态

  + 在 new Promise 的 a 函数内

  + 可以接受两个参数

    1. 第一个参数: 可以将该 Promise 的状态由继续转换为 成功

    2. 第二个参数: 可以将该 Promise 的状态由继续转换为 失败

javascript">// 1. 异步代码
const p = new Promise(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(() => {
    // 下面代码一旦执行, 就会把 promise 的状态改成成功
    // resolve()
    // 下面代码一旦执行, 就会把 promise 的状态改成失败
    // reject()
    if (time >= 3000) {
      // 因为会转换为成功 => 两人去世, 埋一个坟
      // 通知一下, 你该烧纸了
      // resolve() 调用的是 then 内部的函数 b
      // 所以这里书写在 () 内部的 time 内容就是给到 then 内 b 的实参
      resolve(time)
    } else {
      // 当做失败, 表示离婚
      // 通知一下, 该去炸坟了
      // reject() 调用的是 catch 内部的函数 c
      // 所以这里书写在 () 内部的 time 内容就是给到 catch 内 c 的实参, 也是报错信息
      reject(time)
    }
  }, time)
})
// promise 对象调用的两个方法
// 注册 成功
p.then(function b(t) {
  // 函数 b 不会被直接调用的
  // 这个位置的代码会在 p 这个 promise 的状态由 继续 转换为 成功 的时候调用执行
  console.log(t, '成功的函数 b')
  // t 就是你在 promise 内部书写的 resolve 的小括号里面 time 的内容
})
// 注册 失败
p.catch(function c(err) {
  // 函数 c 不会被直接调用
  // 这个位置的代码会在 p 这个 promise 的状态由 继续 转换为 失败 的时候调用执行
  console.log(err, '失败的函数 c')
})

 


4. Promise 的进阶语法

+ 当一个 Promise 的 then 内的代码

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

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

 需求:

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

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

    => 前提: 必须要等到第一个请求结束以后再次发送

javascript">// 初始版 : 
// 需求 1 :
const p = new Promise(function (resolve, reject) {
  // 做异步的事情
  ajax({
    url: 'http://localhost:8888/test/first',
    success: function (res) {
      // 第一个请求成功了
      // res 就是后端给出的结果
      resolve(res)
    }
  })
})
p.then(function (res) {
  console.log('第一次请求结束了')
  console.log(res)
  // 需求2:
  const p2 = new Promise((resolve, reject) => {
    // 做异步的事情
    ajax({
      url: 'http://localhost:8888/test/second',
      dataType: 'json',
      success: function (res) {
        // 第一个请求成功了
        // res 就是后端给出的结果
        resolve(res)
      }
    })
  })
  p2.then(res => {
    console.log('第二个请求结束了')
    console.log(res)
  })
})
javascript">// 进阶版 :
const p = new Promise((resolve, reject) => {
  // 做异步的事情
  ajax({
    url: 'http://localhost:8888/test/first',
    success: function (res) {
      resolve(res)
    }
  })
})
// 向拿到 p 的 resolve 的结果
// 就得写 p.then()
p
  .then(function (res) {
    console.log('第一次请求的结果')
    console.log(res)
    // 做第二个事情
    const p2 = new Promise((resolve, reject) => {
      ajax({
        url: 'http://localhost:8888/test/second',
        dataType: 'json',
        success: function (res) {
          resolve(res)
        }
      })
    })

    // 在第一个 then 内部 return 一个 新的 promise 对象 p2
    return p2
  })
  .then(res => {
    console.log('第二次的请求结果')
    console.log(res)
  })

javascript">// 使用我按照 promise 形式封装的 pAjax 函数来完成
pAjax({ url: 'http://localhost:8888/test/first' })
  .then(res => {
    console.log('第一个请求结束了')
    console.log(res)

    // return 一个新的 promise 对象
    return pAjax({
      url: 'http://localhost:8888/test/second',
      dataType: 'json'
    })
  })
  .then(res => {
    console.log('第二个请求结果')
    console.log(res)

    // return 一个新的 promise 对象
    return pAjax({
      url: 'http://localhost:8888/test/third',
      data: 'name=Jack&age=20',
      dataType: 'json'
    })
  })
  .then(res => {
    console.log('第三次请求的结果')
    console.log(res)
  })

5. async 函数 和 await 关键字

 + ES7 ~ ES8 之间出现的语法

+ 作用 :

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

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

语法:

  => async 关键字 (异步)

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

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

    => async function ( ) { }

    => async ( ) => { }

  + 作用:

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

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

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

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

javascript">// async 的语法
async function fn() {}
const fn = async function () {}
const fn = async a => {}

=> await 关键字 (等待)

  + 要求:

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

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

  + 作用:

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

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

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

javascript">console.log('start')	// ① start
async function fn() {
  console.log('我是 fn 函数内部的代码')	// ②
  // 因为 pAjax 是按照 promise 的语法形式进行封装的代码
  // pAjax 会返回一个 promise 对象
  // fn 函数内, 执行到 pAjax 这个代码的时候
  // 会等待, 等到这个异步的代码完全执行完毕, 把结果赋值给 r1 以后
  // 在继续执行后面的代码
  const r1 = await pAjax({ url: 'http://localhost:8888/test/first' })
  console.log(r1)	// ④
}
fn()
console.log('end')	// ③ end

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

Promise 简图 :


http://www.niftyadmin.cn/n/1498450.html

相关文章

Java重学疑问--为什么float表示的数值范围比long大?

进行混合运算的时候 byte short char 不会相互转换 都会自动提升为int 取值范围大小如下 char < int < long < float <double double的取值范围最大 float的取值范围大于long 虽然 long的长度为8个字节 float的长度为4个字节 但是他们底层的存储结构不同,所代…

win10家庭版调出组策略_【亲测有效】Win10系统彻底禁止自动更新 Win10关闭自动更新方法...

昨天发布了关闭win10自动更新一文&#xff0c;不少用户评论称还是会自动更新&#xff0c;而今天装机之家补充几个设置&#xff0c;亲测有效Win10彻底关闭自动更新&#xff01;&#xff01;&#xff01;不少用户反映自己的Win10系统更新无法彻底关闭&#xff0c;网上提供的关闭W…

html动态浮云效果,基于wordcloud2.js的动态词云

此前接触到的词云板块大都是python的wordcloud模块或者echarts的自浮云&#xff0c;echarts的词云也是基于wordcloud2.js做的扩展&#xff0c;而且之后将之根据三方库封装成了插件,所以在官方示例中也找不到相关案例python中的词云源于WordCloud库&#xff0c;首先需要安装&…

Node _ 初学版

目录 1. 认识 Node 2. Node 基本的命令行操作 3. Node 的模块化开发 1. 自定义模块 2. 内置模块 1. fs 模块 2. path 模块 3. http 模块 4. 认识 npm 5. npm 的基本使用 6. 认识 nrm 简图 : ​ 1. 认识 Node 什么是 Node : > 就是一个 "软件" > c…

今日总结 7/21

1.今天做了很多事情了&#xff0c;上午复习了英语unit11的一半内容&#xff0c;然后白天想了想创新点&#xff0c;创新点这种东西光靠想还是比较难的&#xff0c;虽然有一些idea但是不一定能实现&#xff0c;有点烦。晚上复习了一下昨天学习的Java和之前学习的前端。回宿舍洗个…

小程序数组 显示第一个 点击完成 显示下一个_老师想让同学们练题用哪个小程序可以...

下面给大家介绍下如何使用答题小程序来出题考试&#xff0c;给到学生答题考试的小程序&#xff0c;扫一扫下面的二维码打开这款答题小程序系统就可以使用&#xff0c;无需下载安装&#xff0c;而且是免费使用的。可以用于单位自己组织的答题活动&#xff0c;可以在线出题答题。…

MySQL Workbench--Window安装试用

MySQL Workbench 01、概述 MySQL Workbench provides DBAs and developers an integrated tools environment for: Database Design & ModelingSQL DevelopmentDatabase AdministrationDatabase MigrationThe Community (OSS) Edition is available from this page under t…

手把手教你写Linux设备驱动---中断(三)--workqueue实现(基于友善之臂4412开发板) 【转】...

转自&#xff1a;http://blog.csdn.net/morixinguan/article/details/69680909 上节&#xff0c;我们讲到如何来实现tasklet小任务机制 http://blog.csdn.NET/morixinguan/article/details/69666935 这节&#xff0c;我们来实现一下中断下半部的工作队列: 在写这个demo之前&…