AJAX 进阶 day4

目录

1.同步代码和异步代码

[2.回调函数地狱和 Promise 链式调用](#2.回调函数地狱和 Promise 链式调用)

[2.1 回调函数地狱](#2.1 回调函数地狱)

[2.2 Promise - 链式调用](#2.2 Promise - 链式调用)

[2.3 Promise 链式应用](#2.3 Promise 链式应用)

[3.async 和 await 使用](#3.async 和 await 使用)

[3.1 async函数和await](#3.1 async函数和await)

[3.2 async函数和await_捕获错误](#3.2 async函数和await_捕获错误)

4.事件循环-EventLoop

[4.1 事件循环](#4.1 事件循环)

[4.2 宏任务与微任务- 执行顺序](#4.2 宏任务与微任务- 执行顺序)

[4.3 总结](#4.3 总结)

[5.Promise.all 静态方法](#5.Promise.all 静态方法)

[6.案例 - 商品分类](#6.案例 - 商品分类)

[7.案例 - 学习反馈](#7.案例 - 学习反馈)

[7.1 完成省市区切换效果](#7.1 完成省市区切换效果)

[7.2 收集学习反馈数据,提交保存](#7.2 收集学习反馈数据,提交保存)


1.同步代码和异步代码

同步代码: 逐行 执行,需 原地等待 结果 后,才继续向下执行。
异步代码:调用后 耗时 ,不阻塞代码继续执行(不必原地等待),在 将来 完成后触发一个 回调函数

小结:

  1. 什么是同步代码?
    ➢ 逐行执行, 原地等待 结果 后,才继续向下执行
  2. 什么是异步代码?
    ➢ 调用后 耗时 ,不阻塞代码执行,将来完成后触发 回调函数
  3. JS 中有哪些异步代码?
    ➢ setTimeout / setInterval
    ➢ 事件
    ➢ AJAX
  4. 异步代码如何接收结果?
    ➢ 依靠 回调函数 来接收

2.回调函数地狱和 Promise 链式调用

2.1 回调函数地狱

概念:在回调函数中 嵌套回调函数 ,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获(内层的错误信息,外层无法捕获),耦合性严重,牵一发动全身

案例:

需求:展示默认第一个省,第一个城市,第一个地区在下拉菜单中

2.2 Promise - 链式调用

概念:依靠 then() 方法会返回一个 新生成的 Promise 对象 特性,继续串联下一环任务,直到结束
细节:then() 回调函数中的 返回值 ,会影响新生成的 Promise 对象 最终状态和结果
好处:通过链式调用,解决回调函数嵌套问题

2.3 Promise 链式应用

目标:使用 Promise 链式调用,解决回调函数地狱问题
做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来

代码:

javascript 复制代码
<script>
    /**
     * 目标:把回调函数嵌套代码,改成Promise链式调用结构
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
    */
    let pname = ''

    axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
      // 获取第一个省份
      // console.log(result.data.list[0])
      const province = document.querySelector('.province')
      pname = result.data.list[0]
      province.innerHTML = result.data.list[0]
      // 返回城市的对象
      return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
    }).then(result => {
      // console.log(result.data.list[0])
      // 获取第一个城市
      const city = document.querySelector('.city')
      const cname = result.data.list[0]
      city.innerHTML = result.data.list[0]
      // 返回地区信息
      return axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
    }).then(result => {
      // 获取第一个地区
      const area = document.querySelector('.area')
      area.innerHTML = result.data.list[0]
    })
  </script>

小结:

  1. 什么是 Promise 的链式调用?
    ➢ 使用 then 方法返回新 Promise 对象特性,一直串联下去
  2. then 回调函数中,return 的值会传给哪里?
    ➢ 传给 then 方法生成的新 Promise 对象
  3. Promise 链式调用有什么用?
    ➢ 解决回调函数嵌套问题

3.async 和 await 使用

3.1 async函数和await

目标:掌握async和await语法,解决回调函数地狱

定义:

async 函数是 JavaScript 的一种特殊函数,它可以在函数中使用 await 关键字。当你将一个普通函数用 async 关键字修饰后,这个函数默认返回一个 Promise 对象。普通的返回值会被封装成一个 fulfilled 状态的 Promise 对象,而抛出的任何异常都会被封装成 rejected 状态的 Promise 对象。

概念:在async函数内,使用await关键字,获取Promise对象 **"成功状态"**结果值

注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)

代码:

javascript 复制代码
<script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */

    async function getData() {
      const p = await axios({ url: 'http://hmajax.itheima.net/api/province' })
      const pname = p.data.list[0]
      // console.log(p.data.list[0])
      const c = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
      const cname = c.data.list[0]
      // console.log(c.data.list[0])
      const a = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
      const aname = a.data.list[0]
      console.log(a)

      // 写入html文档
      document.querySelector('.province').innerHTML = pname
      document.querySelector('.city').innerHTML = cname
      document.querySelector('.area').innerHTML = aname

    }

    getData()
  </script>

3.2 async函数和await_捕获错误

语法:

代码:

javascript 复制代码
<script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */

    async function getData() {
      try {
        const p = await axios({ url: 'http://hmajax.itheima.net/api/province' })
        const pname = p.data.list[0]
        // console.log(p.data.list[0])
        const c = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
        const cname = c.data.list[0]
        // console.log(c.data.list[0])
        const a = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
        const aname = a.data.list[0]
        console.log(a)

        // 写入html文档
        document.querySelector('.province').innerHTML = pname
        document.querySelector('.city').innerHTML = cname
        document.querySelector('.area').innerHTML = aname

      } catch (error) {
        console.dir(error)
      }
    }

    getData()
  </script>

捕捉到异常之后,异常后面的代码将不再执行。

4.事件循环-EventLoop

4.1 事件循环

定义:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环。
原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了 事件循环模型。

原理:

  1. 调用栈(Call Stack)

    • 调用栈是执行代码的地方。它记录了当前正在执行的函数和函数调用的历史。当一个函数被调用时,它会被压入调用栈中,执行完成后,它会被从栈中弹出。
  2. 任务队列(Task Queue 或 Callback Queue)

    • 任务队列用于存放异步任务的回调函数。例如,当 setTimeout 函数的定时器结束时,它的回调函数会被放到任务队列中。
  3. 微任务队列(Microtask Queue)

    • 微任务队列存放的是微任务(例如 Promise 的回调函数)。微任务通常比宏任务(如定时器回调)优先级更高。微任务队列的任务会在事件循环的每一轮结束之前执行完毕。

4.2 宏任务与微任务- 执行顺序

异步任务分为:

  • ✓ 宏任务:由浏览器环境执行的异步代码
  • ✓ 微任务:由 JS 引擎环境执行的异步代码


执行顺序:

使用图解-分析代码执行顺序

**简单理解:**同步就是会被立即执行的代码,而异步会被分配到相应的任务队列进行等待,

当栈处于空闲状态就会先清空微任务队列,再执行宏任务队列。

4.3 总结

  1. 什么是宏任务?
  • ➢ 浏览器执行的异步代码
  • ➢ 例如:JS 执行脚本事件,setTimeout/setInterval,AJAX请求完成 事件,用户交互事件等
  1. 什么是微任务?
  • ➢ JS 引擎执行的异步代码
  • ➢ 例如:Promise对象.then()的回调
  1. JavaScript 内代码如何执行?
  • ➢ 执行第一个 script 脚本事件宏任务,里面同步代码
  • ➢ 遇到 宏任务/微任务 交给宿主环境,有结果回调函数进入对应队列
  • ➢ 当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来

5.Promise.all 静态方法

概念:合并多个 Promise 对象,等待所有 同时成功 完成(或某一个失败),做后续逻辑 。

语法:

需求:同时请求"北京","上海","广州","深圳"的天气并在网页尽可能 同时 显示

代码演示:

javascript 复制代码
<body>
  <ul class="my-ul"></ul>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握Promise的all方法作用,和使用场景
     * 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
     * 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
     * code:
     * 北京-110100
     * 上海-310100
     * 广州-440100
     * 深圳-440300
    */
    // 1. 请求城市天气,得到Promise对象
    const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
    const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
    const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
    const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })

    // 2. 使用Promise.all,合并多个Promise对象
    const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
    p.then(result => {
      // 注意:结果数组顺序和合并时顺序是一致
      console.log(result)
      const htmlStr = result.map(item => {
        return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
      }).join('')
      document.querySelector('.my-ul').innerHTML = htmlStr
    }).catch(error => {
      console.dir(error)
    })
  </script>
</body>

6.案例 - 商品分类

商品分类
需求:尽可能同时展示所有商品分类到页面上
步骤:

    1. 获取所有的一级分类数据
    1. 遍历id,创建获取二级分类请求
    1. 合并所有二级分类Promise对象
    1. 等待同时成功,开始渲染页面

效果图:

代码:

javascript 复制代码
<body>
  <!-- 大容器 -->
  <div class="container">
    <div class="sub-list">
      <div class="item">
        <h3>分类名字</h3>
        <ul>
          <li>
            <a href="javascript:;">
              <img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png" />
              <p>巧克力</p>
            </a>
          </li>
          <li>
            <a href="javascript:;">
              <img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png" />
              <p>巧克力</p>
            </a>
          </li>
          <li>
            <a href="javascript:;">
              <img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png" />
              <p>巧克力</p>
            </a>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:把所有商品分类"同时"渲染到页面上
     *  1. 获取所有一级分类数据
     *  2. 遍历id,创建获取二级分类请求
     *  3. 合并所有二级分类Promise对象
     *  4. 等待同时成功后,渲染页面
    */
    axios({
      url: 'http://hmajax.itheima.net/api/category/top'
    }).then(result => {
      // 获取所有一级分类数据
      console.log(result.data.data)

      // 遍历id,创建获取二级分类请求, 这里得到的是所有二级分类的promise对象数组
      const firstObj = result.data.data.map(item => {
        // console.log(item.id)
        return axios({
          url: 'http://hmajax.itheima.net/api/category/sub',
          params: { id: item.id }
        })
      })

      // console.log(firstObj)
      // 合并所有二级分类Promise对象
      const secondObj = Promise.all(firstObj)
      secondObj.then(result => {
        console.log(result)

        // 等待同时成功后,渲染页面
        const thirdObj = result.map(item => {
          const dataObj = item.data.data
          
          // 这里拼接上一级分类和循环遍历一级分类下的二级分类
          return `
             <div class="item">
        <h3>${dataObj.name}</h3>
        <ul>
          
          ${dataObj.children.map(i => {
            return `
                 <li>
            <a href="javascript:;">
              <img src=${i.picture} />
              <p>${i.name}</p>
            </a>
          </li>
              `
          }).join('')}
         
        </ul>
      </div>
          `
        }).join('')

        document.querySelector('.sub-list').innerHTML = thirdObj
      })
    })

  </script>
</body>

7.案例 - 学习反馈

7.1 完成省市区切换效果

步骤:

    1. 设置省份数据到下拉菜单
    1. 切换省份,设置城市数据到下拉菜单,并清空地区下拉菜单
    1. 切换城市,设置地区数据到下拉菜单

页面展示:

代码:

javascript 复制代码
/**
 * 目标1:完成省市区下拉列表切换
 *  1.1 设置省份下拉菜单数据
 *  1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
 *  1.3 切换城市,设置地区下拉菜单数据
 */

// 1.1 设置省份下拉菜单数据


axios({
    url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
    // result得到省份对象
    console.log(result)
    const pnameObj = result.data.list.map(pname => {
        return `<option value="${pname}">${pname}</option>`
    }).join('')
    document.querySelector('.province').innerHTML = '<option value="">省份</option>' + pnameObj
})


let pname = ''

// 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
document.querySelector('.province').addEventListener('change', e => {
    // 获取到当前对象的value值
    // console.log(e.target.value)
    pname = e.target.value
    axios({
        url: 'http://hmajax.itheima.net/api/city',
        params: { pname }
    }).then(result => {
        // result得到城市的对象
        // console.log(result.data.list)
        const cnameObj = result.data.list.map(cname => {
            return ` <option value="${cname}">${cname}</option>`
        }).join('')
        document.querySelector('.city').innerHTML = ' <option value="">城市</option>' + cnameObj
        // 清空地区下拉菜单
        document.querySelector('.area').innerHTML = '<option value="">地区</option>'
    })
})

// 1.3 切换城市,设置地区下拉菜单数据
document.querySelector('.city').addEventListener('change', e => {
    // 获取到当前对象的value值
    // console.log(e.target.value)

    axios({
        url: 'http://hmajax.itheima.net/api/area',
        params: { pname, cname: e.target.value }
    }).then(result => {
        // result得到地区的对象
        // console.log(result.data.list)
        const anameObj = result.data.list.map(aname => {
            return `<option value="${aname}">${aname}</option>`
        }).join('')
        document.querySelector('.area').innerHTML = '<option value="">地区</option>' + anameObj
    })
})

7.2 收集学习反馈数据,提交保存

步骤:

    1. 监听提交按钮的点击事件
    1. 依靠插件收集表单数据
    1. 基于 axios 提交保存,显示结果

页面展示:

代码:

javascript 复制代码
/**
 * 目标2:收集数据提交保存
 *  2.1 监听提交的点击事件
 *  2.2 依靠插件收集表单数据
 *  2.3 基于axios提交保存,显示结果
 */


// 2.1 监听提交的点击事件
document.querySelector('.submit').addEventListener('click', () => {
    // 2.2 依靠插件收集表单数据
    const form = document.querySelector('.info-form')
    const obj = serialize(form, { hash: true, empty: true })
    // 得到表单的提交数据,该数据为一个对象
    // area: "河东区",city: "天津市"
    console.log(obj)

    // 2.3 基于axios提交保存,显示结果
    axios({
        url: 'http://hmajax.itheima.net/api/feedback',
        method: 'POST',
        data: obj
    }).then(result => {
        console.log(result)
        alert(result.data.message)
    }).catch(error => {
        console.dir(error)
        alert(error.response.data.message)
    })
})
相关推荐
LJ小番茄16 分钟前
Vue 常见的几种通信方式(总结)
前端·javascript·vue.js·html
黑狼传说20 分钟前
前端项目优化:极致最优 vs 相对最优 —— 深入探索与实践
前端·性能优化
장숙혜26 分钟前
前端-CDN的理解及CDN一些使用平台
前端
FakeOccupational2 小时前
nodejs 007:错误npm error Error: EPERM: operation not permitted, symlink
前端·npm·node.js
奶糖 肥晨2 小时前
react是什么?
前端·react.js·前端框架
亦舒.2 小时前
JSDelivr & NPM CDN 国内加速节点
前端·npm·node.js
代码搬运媛2 小时前
code eintegrity npm err sha512
前端·npm·node.js
阳光开朗_大男孩儿2 小时前
DBUS属性原理
linux·服务器·前端·数据库·qt
pan_junbiao3 小时前
Vue组件:模板引用ref属性的使用
前端·javascript·vue.js
LvManBa3 小时前
Vue学习记录之四(computed的用法)
前端·vue.js·学习