ajax 进阶

XMLHttpRequest

定义

  • XMLHttpRequest(XHR))对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在 AJAX 编程中被大量使用。
  • axios 内部采用 XMLHttpRequest 与服务器交互

在这里插入图片描述

基本使用

  1. 创建 XMLHttpRequest 对象
  2. 配置请求方法和请求 url 地址
  3. 监听 loadend 事件,接收响应结果
  4. 发起请求

示例代码如下:

// 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 data = JSON.parse(xhr.response) // 手动转为对象
  console.log(data)
})
// 4.发起请求
xhr.send()

查询参数

请求头

使用 URLSearchParams 格式化对象为 get 请求参数,示例代码如下:

// 组织查询参数字符串
const qObj = {
  pname: '浙江省',
  cname: '杭州市'
}
// 查询参数对象 -> 查询参数字符串
const paramsObj = new URLSearchParams(qObj)
const queryString = paramsObj.toString()
console.log(queryString) // pname=&cname=

// 使用XHR对象,查询地区列表
const xhr = new XMLHttpRequest()
xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)
xhr.addEventListener('loadend', () => {
  console.log(xhr.response)
})
xhr.send()

请求体

通过 setRequestHeader 方法设置请求头(Content-Type)表示请求体的内容类型,示例代码如下:

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')
// 准备提交的数据
const userObj = {
  username: 'itheima007',
  password: '7654321'
}
const userStr = JSON.stringify(userObj)
// 设置请求体,发起请求
xhr.send(userStr)

Promise

Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值

在这里插入图片描述

语法

// 1.创建 Promise 对象
const p = new Promise( (resolve, reject) => {
    // 2.执行异步代码
    setTimeout(() => {
        resolve('模拟 Ajax 请求成功结果')
        // reject(new Error('模拟 Ajax 请求失败结果'))
    }, 2000)
})

// 3.获取结果
p.then(result => {
    console.log(result) // 模拟 Ajax 请求成功结果
}).catch(error => {
    console.log(error)
})

三种状态

一个 Promise 对象,必然处于以下几种状态之一:

  • 待定( pending ) :初始状态,既没有被兑现,也没有被拒绝
  • 已兑现( fulfilled ):操作成功完成
  • 已拒绝( rejected ):操作失败

Promise对象一旦被 兑现 / 拒绝 就是已敲定了,状态无法再被改变

在这里插入图片描述

封装简易版 axios

基于 Promise + XMLHttpRequest 封装 myAxios 函数,示例代码如下:

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))
      }
    })
    // 1. 判断有data选项,携带请求体
    if (config.data) {
      // 2. 转换数据类型,在send中发送
      const jsonStr = JSON.stringify(config.data)
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(jsonStr)
    } else {
      // 如果没有请求体数据,正常的发起请求
      xhr.send()
    }
  })
}

document.querySelector('.reg-btn').addEventListener('click', () => {
  // 3. 使用myAxios函数,完成注册用户
  myAxios({
    url: 'http://hmajax.itheima.net/api/register',
    method: 'POST',
    data: {
      username: '校花6666666',
      password: '12345678'
    }
  }).then(result => {
    console.log(result)
  }).catch(error => {
    console.dir(error)
  })
})

Promise.all

Promise.all 静态方法用于合并多个 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'}})

// 合并多个 Promise 对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
  // 返回结果数组的顺序和合并时顺序一致
  console.log(result)
}).catch(error => {
  console.dir(error)
})

解决回调函数地狱

回调函数地狱

概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱

缺点:可读性差,异常无法捕获,耦合性严重

示例代码如下:

// 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 - 链式调用

axios 函数底层基于 Promise,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 new axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
    }
).then(
    result => {
        const areaName = result.data.list[0]
        document.querySelector('.area').innerHTML = areaName
    }
)

async 和 await

  • axios 返回的是一个 Promise 对象,返回的是处理成功后的结果
  • then 方法也是返回的也是一个 Promise 对象,作用见 👆 的链式调用代码
  • 概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
  • await 会阻止 async 异步函数的继续执行,原地等待结果
  • 使用 try...catch 捕获异常

示例代码如下:

// 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 aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
  const areaName = aObj.data.list[0]


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

getData()

同步代码和异步代码

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

JS 中的异步代码:

  • setTimeout / setInterval
  • 事件
  • AJAX

事件循环

由于 js 是单线程的,为了不阻塞 js 引擎,js 引入了事件循环机制,步骤如下:

  1. 执行同步代码,遇到异步代码交给宿主浏览器环境执行
  2. 异步有了结果后,把回调函数放入任务队列排队
  3. 当调用栈空闲后,反复调用任务队列里的回调函数

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值