Ajax_4(进阶)同步异步+ 宏任务微任务 + Promise链 + async终极解决方案 +事件循环原理 + 综合案例

这篇具有很好参考价值的文章主要介绍了Ajax_4(进阶)同步异步+ 宏任务微任务 + Promise链 + async终极解决方案 +事件循环原理 + 综合案例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Ajax(进阶)



01-同步代码和异步代码


什么是同步代码?
  1. 同步代码:逐行执行,需要原地等待结果后,才继续向下执行。

什么是异步代码?
  1. 调用后耗时,不阻塞代码继续执行,(不必原地等待),在将来完成后 触发一个回调函数

代码阅读

目标:阅读并回答代码执行和打印的顺序

<script>   
const result = 0 + 1
 console.log(result)
 setTimeout(() => {
   console.log(2)
 }, 2000)
 document.querySelector('.btn').addEventListener('click', () => {
   console.log(3)
 })
 document.body.style.backgroundColor = 'pink'
 console.log(4)
</script>

打印结果为: 1,4,2,点击按钮一次就打印一次3.

  • 注意,异步代码都有一个特性,那就是耗时的、事件的、通过回调函数返回值。都是异步代码。

02-回调函数地域


概念
  1. 在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地域。

缺点
  1. 可读性差
  2. 异常无法捕获
  3. 耦合性严重,牵一发动全身。

代码示例
 axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
   const pname = result.data.list[0]
   document.querySelector('.province').innerHTML = pname
   // 获取第一个省份的第一个城市
   axios({url: 'http://hmajax.itheima.net/api/city',params: {pname}}).then(result => {
     const cname = result.data.list[0]
     document.querySelector('.city').innerHTML = cname
     // 获取当前城市的第一个地区名字
     axios({url: 'http://hmajax.itheima.net/api/area',params: {pname,cname}}).then(result => {
       const area = result.data.list[0]
       document.querySelector('.area').innerHTML = area
     })
   })
 }).catch(error => {
     console.log(error)
 })
 // 这样一来,就进入了回调地狱了,而且错误无法捕获

  • 回调地域主要就是在回调函数中,不断的使用回到函数。

03-Promise链式调用


概念
  1. 依靠then()方法 会返回一个新 生成的Promise对象特性,继续串联下一环任务,知道结束。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CI9Ao6Nx-1691654693290)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-07_22-29-32.png)]

细节
  1. then()方法里面的回调函数中的返回值,会影响新生成的Promise对象最终状态和结果。

好处
  1. 通过链式通用,解决回调函数嵌套问题。

需求:把省市的嵌套结构,改成链式调用的线性结构

代码示例
 const p = new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('北京市')
   }, 2000)
 })

 //  2、获取省份的名字
 const p2 = p.then(result => {
   console.log(result)
   // 3、创建Promise对象,获取城市名字
   return new Promise((resolve,reject) => {
     setTimeout(() => {
       resolve(result + '---北京')
     },2000)
   })
 })

 // 4、获取城市名字
 p2.then(result => {
   console.log(result)
 })

 // then函数原地的结果是一个新的Promise对象
 console.log(p2 === p)  //地址不一样
</script>

04-Promise链式应用


目标:使用Promise解决链式调用,解决回调函数地域的问题。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YV7efGq0-1691654693291)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-07_23-02-04.png)]

实现方式:每个Promise对象中管理一个一步任务,用then返回Promise对象,串联起来。


代码示例

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

 let pname = ''
 // 1、得到 - 获取省份Promise对象
 axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
   pname = result.data.list[0]
   document.querySelector('.province').innerHTML = pname
   // 2.得到 - 获取城市Promise对象
   return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
 }).then(result => {
   const cname = result.data.list[0]
   document.querySelector('.city').innerHTML = cname
   // 3、得到 -获取地区Promise对象
   return axios({url: 'http://hmajax.itheima.net/api/area',params:{pname, cname}})
 }).then(result => {
   const area = result.data.list[0]
   document.querySelector('.area').innerHTML = area
 })
</script>


05-async函数和await


##### 有什么用呢?
  1. async和await关键字让我们可以用一种更简洁的方式写出基于 Promise的一异步行为,而无需刻意的链式调用Promise.
  2. 解决回调函数地狱

概念
  1. 在async函数内,使用await关键字去掉then函数,等待获取Promise对象成功状态的结果值。
  2. await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
  3. await === Promise对象返回的成功的结果值 (resolve的值)

代码示例
 // 1.定义一个async修饰的函数
 async function getData() {
   // Promise对象的返回成功的结果被await接收。
   const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
   const pname = pObj.data.list[0]

   const cObj = await axios({url: 'http://hmajax.itheima.net/api/city',params: {pname}})
   const cname = cObj.data.list[0]

   const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname}})
   const area = aObj.data.list[0]

   document.querySelector('.province').innerHTML = pname
   document.querySelector('.city').innerHTML = cname
   document.querySelector('.area').innerHTML = area
 }
 getData()

awwit会接受Promise对象返回成功的值。


06-async和await-捕获错误


  1. 使用try catch语句标记需要尝试的语句块,并指定一个出现异常时抛出的响应。
  2. 如果try里某一行代码报错后,try中剩余的代码不会执行了。
  3. catch块,接受错误信息,返回的形参可以查看详细信息。

语法
try {
 //要执行的代码
} catch (error) {
 //error接收的是,错误信息
 //try里的代码,如果有错误,直接进入这里执行
}

07-事件循环(EventLoop)


同步放在执行栈,异步被宿主环境读取,放在任务队列中

由于JavaScript是单线程的,(某一刻只能 执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型。

什么是事件循环
  1. 在执行代码和收集异步任务时,在调用栈空闲时候,反复调用任务队列里的代码执行,就叫做事件循环。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JLX51V2x-1691654693291)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-08_17-19-44.png)]


在JavaScript中,数据类型有分为简单数据类型和引用数据类型,当我们js引擎识别到

  • 简单数据类型 放在栈空间存储值(因为简单数据类型没有地址)
  • 引用数据类型(有地址存储在内存中),所以,引用数据类型地址放在栈空间,值放在堆空间,我们程序员能支配栈空间,堆空间由系统来支配,所以深浅考拷贝的问题就在于地址和值的问题,对于此,我们程序员只能操作引用数据类型的地址,而值,不能直接去改变,会产生很多问题。

事件循环:就是js执行机制分配好存储空间,来决定执行代码

  1. 当代码识别为简单数据类型:则将代码放在执行栈中
  2. 当代码被识别为引用数据类型(复杂数据类型),则将代码放在任务队列中
  • 当执行栈中的代码被执行完毕后,那么js执行机制会向任务队列中读取里面的任务代码,根据所需要执行的事件进行取出任务到执行栈中运行代码。执行完毕后再次读取任务队列中是否还有任务需要执行,如果还有任务,那么继续取出来运行,多次重复的读取操作,就被成为事件循环。

代码体验
    // 体验JS是单线程的,必须先遍历完,才能改变颜色
    document.querySelector('.time-btn').addEventListener('click', () => {
      for (let i = 0; i < 300000; i++) {
        console.log(i)
      }
      document.body.style.background = 'pink'
    })

    // 为了避免线程卡死,出现了异步代码,把耗时操作放到异步中,先保证主线程执行完所有同步代码
    document.querySelector('.time-btn').addEventListener('click', () => {
      setTimeout(() => {
        for (let i = 0; i < 300000; i++) {
          console.log(i)
        }
      }, 0)
      document.body.style.background = 'pink'
    })
<script>
console.log(1)
setTimeout(() =>{
    console.log(2)
},2000)
console.log(3)
//打印顺序:1,3,2

console.log(1)
setTimeout(() => {
    console.log(2)
},0)
console.log(3)
//打印顺序还是: 1,3,2    (因为setTimeout是引用数据类型,放到任务队列中等待执行栈中的同步任务执行完,在读取任务队列中的代码执行,并且一次只能执行一个,多个的话,就重复,这样就形成了事件循环)
</script>

练习
    console.log(1)
    setTimeout(() => {
      console.log(2)
    }, 0)
    function myFn() {
      console.log(3)
    }
    function ajaxFn() {
      const xhr = new XMLHttpRequest()
      xhr.open('GET', 'http://hmajax.itheima.net/api/province')
      xhr.addEventListener('loadend', () => {
        console.log(4)
      })
      xhr.send()
    }
    for (let i = 0; i < 1; i++) {
      console.log(5)
    }
    ajaxFn()
    document.addEventListener('click', () => {
      console.log(6)
    })
    myFn()

    // 答1,5,3,2,4,6(不点击事件不执行)


08-宏任务与微任务


ES6之后引入了Promise对象,让JS引擎也可以发起异步任务。


异步任务分为;

宏任务:由浏览器环境执行的异步代码。

微任务:由JS引擎环境执行的代码

执行顺序:执行栈空闲时,宏任务只有等到所有的微任务执行完毕后才会到宏任务中读取任务。

任务(代码) 执行所在的环境
JS脚本执行事件(script) 浏览器
setTimeout/setlnterval 浏览器
Ajax请求完成事件 浏览器
用户交互事件 浏览器

Promise本身是同步代码,而then和catch回调函数是异步的。放在微任务执行。


代码示例
    console.log(1)
    setTimeout(() => {
      console.log(2)
    }, 0)
    const p = new Promise((resolve, reject) => {
      resolve(3)
    })
    p.then(res => {
      console.log(res)
    })
    console.log(4)

  • 打印顺序为 1 4 3 2

    console.log(1)
    setTimeout(() => {
      console.log(2)
    },0)
    const p = new Promise((resolve,reject) => {
      console.log(3)
      resolve(4)
    })
    p.then(result => {
      console.log(result)
    })
    console.log(5)
  • 执行顺序为: 1 3 5 4 2


09-经典面试题(事件循环)


    // 目标:回答代码执行顺序
    console.log(1)
    setTimeout(() => {
      console.log(2)
      const p = new Promise(resolve => resolve(3))
      p.then(result => console.log(result))
    }, 0)
    const p = new Promise(resolve => {
      setTimeout(() => {
        console.log(4)
      }, 0)
      resolve(5)
    })
    p.then(result => console.log(result))
    const p2 = new Promise(resolve => resolve(6))
    p2.then(result => console.log(result))
    console.log(7)
    // 1 7 5 6 2 3 4

10-Promise.all静态方法


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CpSvq3KJ-1691654693291)(D:\桌面\笔记\Ajax笔记\my-note\Ajax04\images\2023-08-10_09-11-37.png)]

需要同时结果的时候,就是用Promise.all即可。


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


语法
const p = Promise.all([Promise对象,Promise对象,....])
p.then(result => {
    //result结果:[Promise对象成功的结果,Promise对象成功的结果,...]
}).catch(error => {
   //第一个失败的Promise对象,抛出的异常 
})

代码示例
    // 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)
    })

11-商品分类案例


核心步骤:

* 1. 获取所有一级分类数据

* 2. 遍历id,创建获取二级分类请求

* 3. 合并所有二级分类Promise对象

* 4. 等待同时成功后,渲染页面

    //  1. 获取所有一级分类数据
    axios({ url: 'http://hmajax.itheima.net/api/category/top' }).then(result => {
      console.log(result)
      //2. 遍历id,创建获取二级分类请求
      const list = result.data.data.map(item => {
        return axios({
          url: 'http://hmajax.itheima.net/api/category/sub',
          params: {
            id: item.id
          }
        })
      })
      console.log(list)
      //3. 合并所有二级分类Promise对象   Promise 对象就是所有axios函数的返回值
      const p = Promise.all(list)
      p.then(result => {
        console.log(result)
        // 4. 等待同时成功后,渲染页面
        const htmlStr = result.map(item => {
          // 取出关键的数据对象
          const dataObj = item.data.data
          return `<div class="item">
        <h3>分类名字</h3>
        <ul>
          ${dataObj.children.map(item => {
            return `<li>
            <a href="javascript:;">
              <img src="${item.picture}">
              <p>${item.name}</p>
            </a>
          </li>`
          }).join('')}
        </ul>
      </div>`
        }).join('')
        console.log(htmlStr)
        document.querySelector('.sub-list').innerHTML = htmlStr
      })

    })


12-学习反馈案例


* 目标1:完成省市区下拉列表联动切换

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

* 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单

* 1.3 切换城市,设置地区下拉菜单数据


代码示例
// 1.1 设置省份下拉菜单数据
axios({
    url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
    console.log(result)
    // 数组映射
    const optionStr = result.data.list.map(pname => `<option value="${pname}">${pname}</option>`).join()
    document.querySelector('.province').innerHTML = `<option value="">省份</option>` + optionStr
})

// 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
document.querySelector('.province').addEventListener('change', async e => {
    // 立马获取到用户选择的省份名字
    // 拿到省份数据
    const result = await axios({url: 'http://hmajax.itheima.net/api/city',params: { pname: e.target.value}})

    const optionStr = result.data.list.map(cname => `<option value="${cname}">${cname}</option>`).join('')
    // 把默认的城市选项 + 下属城市数据插入到select中
   document.querySelector('.city').innerHTML = `<option value="">城市</option>` + optionStr

    // 清空地区数据
    document.querySelector('.area').innerHTML = `<option value="">地区</option>`
})

// 1.3 切换城市,设置地区下拉菜单数据
document.querySelector('.city').addEventListener('change', async e => {
    const result = await axios({url: 'http://hmajax.itheima.net/api/area',params: {
        pname: document.querySelector('.province').value,
        cname: e.target.value
    }})
    console.log(result)
    const optionStr = result.data.list.map(aname => `<option value="${aname}">${aname}</option>`).join('')

    document.querySelector('.area').innerHTML = `<option value="">地区</option>` + optionStr
})
// 省市区联动效果。2023年8月10日15:36:15

提交表单

* 目标2: 收集数据提交保存

* 2.1 监听提交的点击事件

* 2.2 依靠插件收集表单元素

* 2.3 基于axios提交缓存,显示结果文章来源地址https://www.toymoban.com/news/detail-640053.html


代码示例
// 2.1 监听提交的点击事件
document.querySelector('.submit').addEventListener('click', async () => {
    // 2.2 依靠插件收集表单元素
    const form = document.querySelector('.info-form')
    const data = serialize(form, { hash: true, empty: true })
    console.log(data)
    // 2.3 基于axios提交缓存,显示结果
    try {
        const result = await axios({
            url: 'http://hmajax.itheima.net/api/feedback',
            method: 'POST',
            data//因为接口文档参数名和返回的结果名相同,直接传入data,data配置对象属性名和属性值相同,则简写。
        })
        console.log(result)
        alert(result.data.message)
    } catch(error) {
        console.dir(error)
        alert(error.response.data.message)
    }
})
  1. serialize插件的使用获取表单
  2. async和await修饰函数和获取axios异步响应返回的结果。
  3. 使用try catch(error)来抛出异常,如果响应结果出现问题,那么就返回错误信息。

到了这里,关于Ajax_4(进阶)同步异步+ 宏任务微任务 + Promise链 + async终极解决方案 +事件循环原理 + 综合案例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入理解 Promise、async、回调函数和 AJAX

    简介:本篇博客将介绍 Promise、async、回调函数和 AJAX,这些是在 JavaScript 中处理异步编程和数据交换的关键技术。我们将通过代码示例和解释来详细说明它们的基本用法和优势。 下面是一个简单的示例,展示 Promise 的基本用法: 在创建 Promise 对象时,传递的参数是一个执行

    2024年02月10日
    浏览(30)
  • promise及异步编程async await

    ECMAScript 6 新增了正式的 Promise(期约)引用类型,支持优雅地定义和组织异步逻辑。接下来几个版本增加了使用 async 和 await 定义异步函数的机制 JavaScript 是单线程事件循环模型。异步行为是为了优化因计算量大而时间长的操作,只要你不想为等待某个异步操作而阻塞

    2024年02月04日
    浏览(30)
  • 【js】js 异步机制详解 Generator / Async / Promise

    三种语法功能放在一起,是因为他们都有相似特点: 维护某种状态 在未来恢复状态并执行 本文重点回答以下几个问题: 为什么 Generator 和 Async 函数的 代码执行流 都可以简化成树形结构? async 函数为什么返回一个 promise?返回了怎样一个 promise? async 函数如何优雅的转换成

    2024年01月21日
    浏览(29)
  • 前端面试:【异步编程】Callback、Promise和Async/Await

    嗨,亲爱的JavaScript探险家!在JavaScript开发的旅程中,你会经常遇到异步编程的需求。为了处理异步操作,JavaScript提供了多种机制,包括Callbacks、Promises和Async/Await。本文将深入介绍这些机制,让你能够更好地处理异步任务。 1. Callbacks:传统的异步方式 Callbacks是JavaScript中最早

    2024年02月11日
    浏览(39)
  • 什么是Promise对象?它的状态有哪些?如何使用Promise处理异步操作?以及 async、await

    前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一

    2024年02月11日
    浏览(32)
  • vue async await promise 等待异步接口执行完毕再进行下一步操作

    需求:上传多个文件,每上传一个文件异步请求一下后台接口,并返回一个新文件名,等把所有的异步接口执行成功后,将上传已成功后新文件名数组得到再去更新业务数据 uni-file-picker 文件上传组件的配置 选择文件后,上传到服务器后端,一个一个的传,等异步执行完一下

    2024年02月12日
    浏览(37)
  • 异步回调中axios,ajax,promise,cors详解区分

    Ajax、Promise和Axios之间的关系是,它们都是用于在Web应用程序中发送异步HTTP请求的JavaScript库,但它们有不同的实现方式和用法。 Ajax是一种旧的技术,使用XMLHttpRequest对象来向服务器发送异步请求并获取响应。它通常需要手动编写回调函数来处理响应,并且容易出现回调地狱问

    2024年02月13日
    浏览(29)
  • 异步同步化( Promise 详解)

    ES 6 Promise的用法 一 、为什么要使用Promise “ 回调地狱 ”这个词,不知道大家听过没有,就是异步调用 获取到结果 后, 为下一个异步函数提供 参数 ,所以就会一层一层的出现回调里面 嵌入回调,导致层次很深,代码维护起来特别的复杂,看一下下面的小案例大家就知道什

    2024年02月16日
    浏览(30)
  • Spring之异步任务@Async详解分析

    在 java 中异步线程很重要,比如在业务流处理时,需要通知硬件设备,发短信通知用户,或者需要上传一些图片资源到其他服务器这种耗时的操作,在主线程里处理会阻塞整理流程,而且我们也不需要等待处理结果之后再进行下一步操作,这时候就可以使用异步线程进行处理

    2024年02月01日
    浏览(35)
  • @Async异步线程:Spring 自带的异步解决方案

            在项目应用中,使用MQ异步调用来实现系统性能优化,完成服务间数据同步是常用的技术手段。如果是在同一台服务器内部,不涉及到分布式系统,单纯的想实现部分业务的异步执行,这里介绍一个更简单的异步方法调用。         对于异步方法调用,从Spring3 开

    2023年04月24日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包