AJAX 3——原理:XMLHttpRequest+Promise

AJAX 3------原理

1.XMLHttpRequest

  • 定义:XHR用于与服务器交互,通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL,获取数据。

  • axios与XHR关系:axios使用XMLHttpRequest与服务器沟通

  • 使用XMLHttpRequest

    • 创建XMLHttpRequest对象
    • 配置请求方法和URL网址
    • 监听loadend事件,接收响应结果
    • 发起请求
    js 复制代码
    // 1. 创建 XMLHttpRequest 对象
        const xhr = new XMLHttpRequest()
    // 2. 配置请求方法和请求 url 地址
        xhr.open('GET', 'http://hmajax.itheima.net/api/province')
    // 3. 监听 loadend 事件,接收响应结果
        xhr.addEventListener('loadend', () => {
          console.log(xhr.response)
          //对响应结果进行处理
          const obj = JSON.parse(xhr.response)
          console.log(obj)
          const str = obj.list.join('<br>')
          document.querySelector('p').innerHTML = str
    
        })
    // 4. 发起请求
        xhr.send()
  • XMLHttpRequest------查询参数

    js 复制代码
     const pname = document.querySelector('[name=province]').value
          const cname = document.querySelector('[name=city]').value
          //组织查询字符串 防止参数过多一个一个添加太麻烦
          const qObj = {
            pname,
            cname
          }
          const paramsObj = new URLSearchParams(qObj)
          const queryString = paramsObj.toString()
          console.log(queryString);
          //使用xhr
          const xhr = new XMLHttpRequest()
          xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)
          xhr.addEventListener('loadend', () => {
            console.log(xhr.response)
            const obj = JSON.parse(xhr.response)
            console.log(obj)
            const str = obj.list.map((item) => {
              return `
                <li class="list-group-item">${item}</li>
                `
            }).join('')
            document.querySelector('.list-group').innerHTML = str
    
          })
          xhr.send()
  • XMLHttpRequest------提交数据(通过请求体)

    js 复制代码
     document.querySelector('.reg-btn').addEventListener('click', () => {
          const xhr = new XMLHttpRequest()
          xhr.open('POST', 'http://hmajax.itheima.net/api/register')
          xhr.addEventListener('loadend', () => {
            console.log(xhr.response);
          })
          //告诉服务器,我传递的内容类型,是JSON字符串
          xhr.setRequestHeader('Content-Type', 'application/json')
          //准备数据并转成JSON字符串
          const user = { username: 'lyq123456', password: '123456' }
          const userStr = JSON.stringify(user)
          xhr.send(userStr)
        })

2.Promise

  • 定义:Promise对象用于管理一个异步操作最终状态和结果值
  • axios与Promise关系:axios利用Promise管理异步通信的状态和结果
  • 好处
    • 逻辑更清晰
    • 了解axios函数内部运作机制
    • 解决回调函数地狱问题
  • 语法
js 复制代码
//1.创建Promise对象
    const p = new Promise((resolve, reject) => {
//2.执行异步代码 成功用resolve,失败用reject
      setTimeout(() => {
        // resolve('模拟AJAX请求-成功结果')
        reject(new Error('模拟AJAX请求-失败结果'))
      }, 2000)
    })
//3.获取结果
    p.then(result => {
      console.log(result)
    }).catch(error => {
      console.log(error)
    })
  • 三种状态

    • 待定(pending):初始状态,既没有被兑现,也没有被拒绝
    • 已兑现(fulfilled):操作成功,调用resolve()后,触发.then()
    • 已拒绝(rejected) :操作失败,调用reject()后,触发.catch()
  • 使用Promise管理XHR请求省份列表

js 复制代码
<p class="my-p"></p>
  <script>
    /**
     * 目标:使用Promise管理XHR请求省份列表
     *  1. 创建Promise对象
     *  2. 执行XHR异步代码,获取省份列表
     *  3. 关联成功或失败函数,做后续处理
    */
    const p = new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest()
      xhr.open('GET', 'http://hmajax.itheima.net/api/province')
      xhr.addEventListener('loadend', () => {
        //判断成功还是失败------通过状态码 
        // 2XX开头都是成功
        console.log(xhr)
        console.log(xhr.response)
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(JSON.parse(xhr.response))
        } else {
          reject(new Error(xhr.response))
        }
      })
      xhr.send()
    })
    p.then(result => {
      console.log(result)
      const str = result.list.join('<br>')
      document.querySelector('.my-p').innerHTML = str
    }).catch(error => {
      //错误对象要用console.dir详细打印
      console.dir(error)
      document.querySelector('.my-p').innerHTML = error.message

    })
  </script>
  • 若有查询参数,添加以下代码

    js 复制代码
     if (config.params) {
              //组织查询字符串 
              const paramsObj = new URLSearchParams(config.params)
              const queryString = paramsObj.toString()
              //把查询参数字符串接在url?后面
              config.url += `?${queryString}`
            }
  • 若需要传递请求体数据,添加以下代码:

    js 复制代码
      if (config.data) {
              const jsonStr = JSON.stringify(config.data)
              //告诉服务器,我传递的内容类型,是JSON字符串
              xhr.setRequestHeader('Content-Type', 'application/json')
              xhr.send(jsonStr)
            } else {
              xhr.send()
            }

3.使用XMLHttpRequest+Promise封装一个简易的axios函数

js 复制代码
 function myAxios(config) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        if (config.params) {
          const paramsObj = new URLSearchParams(config.params)
          const queryString = paramsObj.toString()
          config.url += `?${queryString}`
        }
        xhr.open(config.method || 'GET', config.url)

        xhr.addEventListener('loadend', () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(JSON.parse(xhr.response))
          } else {
            reject(new Error(xhr.response))
          }
        })
        if (config.data) {
          const jsonStr = JSON.stringify(config.data)
          //告诉服务器,我传递的内容类型,是JSON字符串
          xhr.setRequestHeader('Content-Type', 'application/json')
          xhr.send(jsonStr)
        } else {
          xhr.send()
        }

      })
    }

4.回调函数地狱及Promise链式调用

  • 回调函数地狱:在回调函数中嵌套回调函数,
  • 缺点:可读性差,里面异常无法捕获,耦合性严重
js 复制代码
// 1. 获取默认第一个省份的名字
    axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
      const pname = result.data.list[0]
      document.querySelector('.province').innerHTML = pname
// 2. 获取默认第一个城市的名字
      axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }).then(result => {
      const cname = result.data.list[0]
      document.querySelector('.city').innerHTML = cname
 // 3. 获取默认第一个地区的名字
        axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }).then(result => {
      console.log(result)
      const areaName = result.data.list[0]
      document.querySelector('.area').innerHTML = areaName
        })
      })
    }).catch(error => {
      console.dir(error)
    })
  • Promise链式调用
    • 定义:依靠then()方法会返回一个新生成的Promise对象特性,继续串联下一环任务
    • 注意then()回调函数中的返回值会影响新生成的Promise对象最终状态和结果
    • 作用:解决回调函数地狱问题

eg:Promise链式调用:

js 复制代码
/**
     * 目标:掌握Promise的链式调用
     * 需求:把省市的嵌套结构,改成链式调用的线性结构
    */
    //1.创建省份名字
    const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('北京市')
      }, 2000)
    })
    //2.获取省份名字
    const p2 = p.then(result => {
      console.log(result)
      //创建Promise对象
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(result + '---北京')
        }, 2000)
      })
    })
    //3.获取城市名字
    p2.then(result => {
      console.log(result)
    })
    //then()原地的结果是一个新的Promise对象
    console.log(p2 === p) //false

eg:使用Promise链式调用解决回调函数地狱问题

js 复制代码
<body>
  <form>
    <span>省份:</span>
    <select>
      <option class="province"></option>
    </select>
    <span>城市:</span>
    <select>
      <option class="city"></option>
    </select>
    <span>地区:</span>
    <select>
      <option class="area"></option>
    </select>
  </form>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:把回调函数嵌套代码,改成Promise链式调用结构
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
    */
    let pname = ''
    //获取省份的promise对象
    axios({
      url: 'http://hmajax.itheima.net/api/province'
    }).then(result => {
      console.log(result);
      pname = result.data.list[0]
      document.querySelector('.province').innerHTML = pname
      return axios({
        url: 'http://hmajax.itheima.net/api/city', params: { pname }
      })
    }).then(result => {
      console.log(result);
      const cname = result.data.list[0]
      document.querySelector('.city').innerHTML = cname
      return axios({
        url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }
      })
    }).then(result => {
      console.log(result);
      const areaName = result.data.list[0]
      document.querySelector('.area').innerHTML = areaName
    })

  </script>
</body>

5.async函数和await

  • 定义:async是使用async关键字声明的函数,是AsyncFunction构造函数的实例。async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,无需刻意的链式调用promise

  • 使用async函数和await解决回调函数地狱

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

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

js 复制代码
<script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */
   //1.定义async修饰函数
    async function getData() {
   //2.await等待Promise对象成功的结果
      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 areaObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
      const areaName = areaObj.data.list[0]
      document.querySelector('.area').innerHTML = areaName
      document.querySelector('.province').innerHTML = pname
      document.querySelector('.city').innerHTML = cname
    }
    getData()
  </script>

6.宏任务和微任务(异步任务分类)

  • 宏任务:由浏览器环境执行的异步代码,eg:JS脚本执行事件,setTimeout/setInterval、AJAX请求完成事件,用户交互事件

  • 微任务:由JS引擎环境执行的异步代码,eg:Promise对象.then()'

  • 代码执行顺序:优先调度微任务队列,再调度宏任务队列

7.Promise.all静态方法

  • 定义:合并多个Promise对象,等待所有同时成功完成,做后续逻辑
js 复制代码
    const bj = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
    const sh = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
    const gz = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
    const sx = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })

    const p = Promise.all(bj, sh, gz, sx)
    p.then(result => {
     //对返回的数组结果进行操作
      console.log(result)
    }).catch(error => {
      console.dir(error)
    })
相关推荐
weixin_382395233 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__3 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.4 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~5 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华6 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子8 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅8 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni8 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学10 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端