Ajax-1

同步与异步

同步代码

  • 逐行执行,需要原地等待结果后,才继续向下执行

异步代码

  • 调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数

认识 AJAX

什么是 AJAX

  • AJAX 是 异步的JavaScript和XML [Asynchronous JavaScript And XML]
  • 简单点说,就是使用XMLHttpRequest对象与服务器通信
  • 它可以使用 JSON, XML, HTML 和 text 文本等格式发送和接收数据
  • AJAX 最吸引人的是它"异步"的特性,也就是说它能在不刷新页面的情况下与服务器进行,交互数据,或更新页面

如何使用 AJAX

  • 先学会使用axios库,与服务器进行数据交互
  • 再学习XMLHttpRequest对象的使用,了解 AJAX的底层原理

使用 axios

基本使用

语法:
	1.引入 axios.js:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
	2.使用 axios 函数:
		2.1 传入配置对象
		2.2 使用 .then 回调函数接收结果,并做后续处理
<body>
    <!-- 引入 axios.js 库 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        axios({
     
            url: 'http://datavmap-public.oss-cn-hangzhou.aliyuncs.com/areas/csv/100000_province.json',
        }).then((result) => {
      console.log(result.data.rows) })
    </script>
</body>

核心配置

请求方法

<body>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        axios({
            url: 'http://datavmap-public.oss-cn-hangzhou.aliyuncs.com/areas/csv/100000_province.json',
            
            // 常用请求方法: get、post
            method: 'get'   // 由于默认请求方法是get,因此此行可以省略不写
        }).then((result) => { console.log(result.data.rows) })
    </script>
</body>

查询参数

<body>
    <ul></ul>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        axios({
     
            url: 'https://hmajax.itheima.net/api/city',
            method: 'get',
            params: {
     
                pname: prompt('请输入省份:')
            },
        }).then((result) => {
     
            // 筛选有效的数据
            cityArr = result.data.list
            // 将数据展示到页面上
            const str = cityArr.map(ele => `<li>${ ele}</li>`).join('')
            document.querySelector('ul').innerHTML = str
        })
    </script>
</body>

提交数据

<body>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        axios({
     
            url: 'https://hmajax.itheima.net/api/register',
            method: 'post',
            // 要提交的数据
            data: {
     
                username: prompt('请输入注册账号'),
                password: prompt('请输入密码')
            }
        }).then((result) => {
     
            alert(result.data.message)
        })
    </script>
</body>

错误处理

处理格式

    <script>
        axios({
     
            //请求选项
        }).then(() => {
     
            // 处理数据
        }).catch(error => {
     
            // 处理错误
        })
    </script>

具体演示

<body>
    <div>
        <label>注册账号:<input type="text"></label><br>
        <label>填写密码:<input type="password"></label>
    </div>
    <button>注册</button>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        const input = document.querySelector('input[type=text]')
        const password = document.querySelector('input[type=password]')
        const button = document.querySelector('button')

        button.addEventListener('click', () => {
     
            
            axios({
     
                url: 'https://hmajax.itheima.net/api/register',
                method: 'post',
                // 要提交的数据
                data: {
     
                    username: input.value,
                    password: password.value
                }
            }).then((result) => {
     
                alert(result.data.message)
            }).catch(error => {
     
                alert(error.response.data.message)
            })

        })
    </script>
</body>

XMLHttpRequest

认识 XHR

  • XHRXMLHttpRequest
  • XMLHttpRequest 对象用于与服务器交互
  • 通过 XMLHttpRequest 可以在不刷新页面的情况下,请求特定URL,获取数据
  • 这允许网页在不影响用户操作的情况下,更新页面的局部内容
  • XMLHttpRequest 在 AJAX 编程中被大量使用
  • 我们使用的 axios 是一个封装好的库,它的底层使用 XMLHttpRequest 与服务器交互

基本使用

<body>
    <ul></ul>

    <script>
        // 1. 创建XMLHttpRequset对象
        const xhr = new XMLHttpRequest()
        // 2. 配置请求方法和URL地址
        xhr.open('GET', 'https://hmajax.itheima.net/api/province')
        // 3. 提前准备好一个监听loadend事件,用来接收响应结果
        xhr.addEventListener('loadend', () => {
     
            // 数据处理与筛选
            const result = JSON.parse(xhr.response)
            const arr = result.list
            // 将数据渲染到页面
            document.querySelector('ul').innerHTML =
                arr.map(ele => `<li>${ ele}</li>`).join('')
        })
        // 4.向服务端发送请求
        xhr.send()
    </script>
</body>

查询参数

<body>
    <ul></ul>

    <script>
        // 目标:使用XHR携带查询参数
        const xhr = new XMLHttpRequest()
        const goal = prompt('您要搜索的城市:')
        xhr.open('GET', `https://hmajax.itheima.net/api/city?pname=${ goal}`)
        xhr.addEventListener('loadend', () => {
     
            const result = JSON.parse(xhr.response
            const arr = result.list
            // 将数据渲染到页面
            document.querySelector('ul').innerHTML =
                arr.map(ele => `<li>${ ele}</li>`).join('')
        })
        xhr.send()
    </script>
</body>

数据提交

<body>
    <button class="reg-btn">注册用户</button>

    <script>
        // 目标:使用XHR实现数据提交,完成注册功能
        document.querySelector('.reg-btn').addEventListener('click', () => {
     
            const xhr = new XMLHttpRequest()
            xhr.open('POST', 'https://hmajax.itheima.net/api/register')
            xhr.addEventListener('loadend', () => {
     
                const result = JSON.parse(xhr.response)
                console.log(result)
            })
            
            // xhr设置请求头:告诉服务器内容类型(JSON字符串)
            xhr.setRequestHeader('Content-Type', 'application/json')
            // 准备要提交的数据
            const dataObj = {
     
                username: 'jackJack',
                password: '123456'
            }
            const dataStr = JSON.stringify(dataObj)     // 将数据对象转为JSON字符串
            // 设置好"请求体",并发送请求
            xhr.send(dataStr)
        })
    </script>
</body>

Promise

基本认识

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

    <script>
        // 1.创建Promise对象
        const p = new Promise((resolve, reject) => {
     
            
            // 2.执行异步任务
            // 任务执行成功,就调用:resolve(...) 从而触发 then() 执行
            // 任务执行失败,就调用:reject(...) 从而触发 catch() 执行
        })

        // 3.接收结果
        p.then(result => {
     
            // 成功
        }).catch(error => {
     
            // 失败
        })
    </script>

Promise 对象有三种状态

  • 待定状态------pending
  • 已兑现状态—fulfilled
  • 已拒绝状态—rejected
    <script>
        // 1.创建Promise对象(此时Promise对象处于pending状态,即:待定状态)
        const p = new Promise((resolve, reject) => {
     

            // Promise对象创建时,这里的代码都会执行了
            console.log('是这里先执行吗?');

            setTimeout(() => {
     
                
                resolve('模拟AJAX请求:结果成功!')   // 当执行完resolve(),此时Promise对象处于fulfilled状态,即:已兑现状态


                // reject(new Error('模拟AJAX请求:结果失败!'))    // 当执行完reject(),此时Promise对象处于rejected状态,即:已拒绝状态

            }, 2000)
        })
        console.log('还是说,这里先执行呢?')

        // 3.获取结果
        p.then(result => {
     
            console.log(result);
        }).catch(error => {
     
            console.log(error);
        })
    </script>

具体使用

需求:使用 Promise + XHR 获取省份列表

<body>
    <ul></ul>

    <script>
        // 1. 创建Promise对象
        const p = new Promise((resolve, reject) => {
     
            // 2. 执行XHR异步代码,获取省份列表
            const xhr = new XMLHttpRequest()
            xhr.open('GET', 'https://hmajax.itheima.net/api/province')
            xhr.addEventListener('loadend', () => {
     
                // console.log(xhr.status)  // 响应状态码
                if (xhr.status >= 200 && xhr.status < 300) {
     
                    resolve(JSON.parse(xhr.response))
                } else {
     
                    reject(new Error(xhr.response))
                }
            })
            xhr.send()
        })

        // 正常要执行的代码(非异步的)
        console.log('假设这里')
        console.log('有着很多很多的')
        console.log('代码需要执行')
        console.log('-------------')

        // 3. 返回结果
        p.then(result => {
     
            // 先给用户提前告知一声结果
            alert('省份数据获取成功!')
            // 然后将数据渲染到页面上
            arr = result.list
            document.querySelector('ul').innerHTML =
                arr.map(ele => `<li>${ ele}</li>`).join('')
        }).catch(error => {
     
            // 先给用户提前告知一声结果
            alert('省份数据获取失败!')
            // 错误对象要用console.dir详细打印
            console.dir(error)
            // 并且将关键的错误信息,显示到页面给用户看
            document.querySelector('ul').innerHTML = `${ error.message}`
        })

        // 正常要执行的代码(非异步的)
        console.log('假设这里')
        console.log('有着很多很多的')
        console.log('代码需要执行')
        console.log('-------------')
    </script>
</body>

回调函数地狱

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

缺点:

  • 代码可读性差
  • 异常无法捕获
  • 耦合性严重,牵一发动全身

例子:如下

<body>
    <div>
        <div class="province"></div>
        <div class="city"></div>
        <div class="area"></div>
    </div>
    
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // 获取省份
        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: pname } }).then(result => {
     
                const cname = result.data.list[0]
                document.querySelector('.city').innerHTML = cname
                // 获取地区
                axios({
      url: 'http://hmajax.itheima.net/api/area', params: {
      pname: pname, cname: cname } }).then(result => {
     
                    const area = result.data.list[0]
                    document.querySelector('.area').innerHTML = area
                })
            })
        })
    </script>
</body>

链式调用

基本认识

  • 一个 Promise对象 在它的 then() 方法 里可以 return 一个 新的 Promise对象

  • 利用好这个特性,就能串联下一个任务,实现链式调用

<body>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        const p1 = new Promise((resolve, reject) => {
     
            // 模拟请求
            setTimeout(() => {
     
                resolve('湖北省')   // 2秒之后服务器成功返回了数据:"湖北省",通过resolve()传递给then()
            }, 2000)
        })

        const p2 = p1.then(result => {
     
            console.log(result)     // 输出传递过来的结果
            return new Promise((resolve, reject) => {
     
                // 模拟请求
                setTimeout(() => {
     
                    resolve('武汉市')   // 2秒之后服务器成功返回了数据:"武汉市",通过resolve()传递给then()
                }, 2000)
            })
        })

        const p3 = p2.then(result => {
     
            console.log(result) // 输出传递过来的结果
            return new Promise((resolve, reject) => {
     
                // 模拟请求
                setTimeout(() => {
     
                    resolve('武昌区')   // 2秒之后服务器成功返回了数据:"武昌区",通过resolve()传递给then()
                }, 2000)
            })
        })

        p3.then(result => {
     
            console.log(result) // 输出传递过来的结果
        })
    </script>
</body>

解决回调函数地狱

<body>
    <ul></ul>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // 防止变量污染
        (function () {
     

            // 定义变量,用来存储数据
            let province
            let city
            let area

            // 利用"链式调用"解决"回调函数地狱"
            let obj = axios({
      url: 'http://hmajax.itheima.net/api/province' })  // 请求获取省份数据

            obj = obj.then(result => {
     
                console.log(result)     // 输出省份数据
                province = result.data.list[18]
                return axios({
      url: 'http://hmajax.itheima.net/api/city', params: {
      pname: province } })  // 请求获取城市数据
            })

            obj = obj.then(result => {
     
                console.log(result)     // 输出城市数据
                city = result.data.list[0]
                return axios({
      url: 'http://hmajax.itheima.net/api/area', params: {
      pname: province, cname: city } })  // 请求获取地区数据
            })

            obj.then(result => {
     
                console.log(result)     // 输出地区数据
                area = result.data.list[0]

                // 将数据显示到页面上
                document.querySelector('ul').innerHTML = `<li>${ province}</li><li>${ city}</li><li>${ area}</li>`
            })
        })()
    </script>
</body>

Promise.all 静态方法

基本认识

概念:合并多个Promise对象,等待所有的Promise对象完成,才进行后续逻辑

  • 所有的Promise对象都成功,全体才算成功
  • 任意一个Promise对象失败了,全体都失败了

语法:如下

    <script>
        const p = Promise.all([Promise对象, Promise对象, ...])
        p.then(result => {
     
            // result结果: [Promise对象成功的结果, Promise对象成功的结果, ...]
        }).catch(error => {
     
            // 最先失败的Promise对象,抛出的异常
        })
    </script>

具体演示

<body>
    <ul></ul>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // 目标:实现将北京、上海、广州、深圳的天气同时展示到页面上

        // 1. 发起请求,得到Promise对象
        const bjPromise = axios({
      url: 'http://hmajax.itheima.net/api/weather', params: {
      city: '110100' } })   // 北京Promise对象
        const shPromise = axios({
      url: 'http://hmajax.itheima.net/api/weather', params: {
      city: '310100' } })   // 上海Promise对象
        const gzPromise = axios({
      url: 'http://hmajax.itheima.net/api/weather', params: {
      city: '440100' } })   // 广州Promise对象
        const szPromise = axios({
      url: 'http://hmajax.itheima.net/api/weather', params: {
      city: '440300' } })   // 深圳Promise对象

        // 2. 使用Promise.all,合并多个Promise对象
        const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
        p.then(result => {
     
            console.log(result)
            // 将数据渲染到页面上
            document.querySelector('ul').innerHTML =
                result.map(ele => {
     
                    const info = ele.data.data
                    return `<li>[地区: ${ info.area}]-[时间: ${ info.date}]-[天气: ${ info.weather}]</li>`
                }).join('')
        }).catch(error => {
     
            console.dir(error)
        })
    </script>
</body>

简易版 axios (过程)

需求:基于 Promise + XHR 封装 myAxios 函数

  • 回顾 axios 的功能
<body>
    <ul></ul>
    
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        axios({
     

            url: 'https://hmajax.itheima.net/api/province',
            method: 'get'

        }).then(result => {
     

            // console.log(result)             // 返回数据对象
            // console.log(result.data.list)   // 筛选有效信息
            
            // 将数据渲染到页面
            const arr = result.list
            document.querySelector('ul').innerHTML =
                arr.map(ele => `<li>${ ele}</li>`).join('')

        }).catch(error => {
     
            console.log(error)
        })
    </script>
</body>
  • 制作简易版的 axios
<body>
    <script>
        function myAxios(configObj) {
     
            // ------------函数体--------------

            // 函数第一步:创建Promise对象
            const p = new Promise((resolve, reject) => {
     

                // ----------------异步代码---------------------

                // 1.1 判断configObj是否有params属性
                if (configObj.params) {
     
                    // 1.2 使用URLSearchParams将"查询参数的对象"转换成"符合查询参数格式的字符串"
                    const paramsObj = new URLSearchParams(configObj.params)
                    const queryString = paramsObj.toString()
                    // 1.3 把查询参数字符串,拼接到URL后面
                    configObj.url += `?${ queryString}`
                }

                // 2.1 准备好一个用于与服务器连接的xhr对象
                const xhr = new XMLHttpRequest()
                // 2.2 给xhr配置相关信息
                xhr.open(configObj.method, configObj.url)
                xhr.addEventListener('loadend', () => {
     
                    if (xhr.status >= 200 && xhr.status < 300) {
     
                        resolve(JSON.parse(xhr.response))
                    } else {
     
                        reject(new Error(xhr))
                    }
                })

                // 3.1 判断是否有data选项
                if (configObj.data) {
     
                    // 3.2 转换数据类型,准备好"数据体"
                    const jsonStr = JSON.stringify(configObj.data)
                    // 3.3 配置好"数据头"
                    xhr.setRequestHeader('Content-Type', 'application/json')
                    // 3.4 携带数据,向服务器发送"请求"
                    xhr.send(jsonStr)
                } else {
     
                    // 3.5 如果没有data选项,直接发送"请求"即可
                    xhr.send()
                }

                // ----------------异步代码---------------------
            })

            // 函数第二步:返回Promise对象
            return p

            // ------------函数体--------------
        }
    </script>
</body>

简易版 axios (模板)

为了方便日后使用简易版的 axios,现将它简化压缩成下面代码(用法不变)

    function simpleAxios(config) {
        return new Promise((resolve, reject) => {
            config.params && (config.url += `?${new URLSearchParams(config.params).toString()}`)
            const xhr = new XMLHttpRequest()
            xhr.open(config.method, 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) {
                xhr.setRequestHeader('Content-Type', 'application/json')
                xhr.send(JSON.stringify(config.data))
            } else { xhr.send() }
        })
    }

新方法实现异步

async 与 await

<body>
    <div>
        <div class="province"></div>
        <div class="city"></div>
        <div class="area"></div>
    </div>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // 1. 定义 async 修饰函数
        async function getData() {
     
            // 1.1. await 等待 Promise 对象成功的结果
            // await 必须用在 async 修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)

            const pObj = await axios({
      url: 'http://hmajax.itheima.net/api/province' })
            const province = pObj.data.list[0]

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

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

            // 1.2 将数据渲染到页面上
            document.querySelector('.province').innerHTML = province
            document.querySelector('.city').innerHTML = city
            document.querySelector('.area').innerHTML = area
        }

        // 2. 调用函数
        getData()
    </script>
</body>

错误捕获

<body>
    <div>
        <div class="province"></div>
        <div class="city"></div>
        <div class="area"></div>
    </div>

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        async function getData() {
     
            try {
     
                const pObj = await axios({
      url: 'http://hmajax.itheima.net/api/province' })
                const province = pObj.data.list[0]

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

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

                document.querySelector('.province').innerHTML = province
                document.querySelector('.city').innerHTML = city
                document.querySelector('.area').innerHTML = area

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

        // 调用函数
        getData()
    </script>
</body>

相关推荐

  1. Ajax-1

    2024-02-05 03:24:01       46 阅读
  2. AJAX:整理1: 了解AJAX的相关知识

    2024-02-05 03:24:01       59 阅读
  3. Ajax

    2024-02-05 03:24:01       52 阅读
  4. Ajax

    2024-02-05 03:24:01       53 阅读
  5. <span style='color:red;'>AJAX</span>.

    AJAX.

    2024-02-05 03:24:01      55 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-05 03:24:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-05 03:24:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-05 03:24:01       82 阅读
  4. Python语言-面向对象

    2024-02-05 03:24:01       91 阅读

热门阅读

  1. 平台+低代码:中小企业数字化转型普惠之路

    2024-02-05 03:24:01       61 阅读
  2. 03-OpenFeign-请求传参设置

    2024-02-05 03:24:01       52 阅读
  3. 超全总结!大模型算法岗面试指南来了!

    2024-02-05 03:24:01       57 阅读
  4. (c语言版)智能成绩表

    2024-02-05 03:24:01       47 阅读
  5. 详细解说MySQL中data_sub()函数

    2024-02-05 03:24:01       38 阅读
  6. QEMU搭建Linux-ARM系统

    2024-02-05 03:24:01       53 阅读
  7. C# CAD界面介绍(一)

    2024-02-05 03:24:01       53 阅读
  8. Kotlin-类

    2024-02-05 03:24:01       53 阅读
  9. 1162. 地图分析

    2024-02-05 03:24:01       59 阅读