美食家大橙子 发表于 2025-1-8 04:39:46

从AJAX到Promise再到Axios

ajax


异步交互

首先我先讲讲为什么需要 ajax,因为我们需要异步交互,那我们为什么需要异步交互呢?
我以为异步交互有两个长处:
1. 相比于同步交互,异步交互可以更大程度地提高服务器的使用服从,不至于让服务器有空闲的时候。
https://i-blog.csdnimg.cn/direct/06d251fe44dd4e7c9f66be2beb621691.png拓展一个知识点:js是单线程语言,欣赏器只分配给js一个主线程,用来执行任务。但是js却是可以实现异步,这得益于欣赏器帮助js实现的。详细内容可以看JavaScript的单线程和异步 - 知乎

2. 异步保护 url 信息
假如你使用 get 方法向后端发送 http 请求,则请求所附带的参数会跟在 url 的 ?后,此时假如你使用的是公共的 wifi,则别人可以获取你的 url,进而就有大概根据你的 url 来获取到你的相关信息,比如登录密码等。
你有两种解决方式:


[*]一种是使用 post 请求方式,这种方式会把请求的参数信息放到请求体中,而请求体一样平常是颠末加密的。
[*]一种是使用异步交互,异步交互不会改变 url,即
异步请求通常不会直接改变欣赏器地点栏中的 URL。这是因为这些请求是独立于欣赏器的传统同步请求(如直接在地点栏输入 URL 或点击链接)发起的。
异步请求的特点:


[*] 独立于欣赏器导航:异步请求不会使欣赏器加载新页面或重新加载当前页面。它们在背景与服务器交换数据,而不影响当前页面的状态或URL。
[*]不改变 URL:这些请求不会改变欣赏器地点栏中的 URL,因为它们不触发欣赏器的导航行为。用户在地点栏中看到的 URL 保持不变。
[*]更新页面内容:只管异步请求不直接改变 URL,但它们可以用于更新页面的某部分内容。例如,获取最新的用户评论或股票代价,并在不刷新整个页面的环境下更新这些信息。




异步与回调函数

异步操作需要一种机制来关照调用者操作何时完成。而回调函数就是这种关照机制之一。




XMLHttpRequest 对象

Ajax 通过 XMLHttpRequest 对象来实现异步交互,具体的怎么实现的在 Ajax 代码实现中讲述,这里我们先学习 XMLHttpRequest 对象是什么。


xhr,全称为 XMLHttpRequest,用于与服务器交互数据,是 ajax 功能实现所依赖的对象。
当客户端发出请求时,请求数据发送给 XMLHttpRequest 而不是直接发送给服务器。而且请求是异步发送的。
而且,服务器不在将数据直接返回给客户端欣赏器,而是返回给 XMLHttpRequest 对象。
XMLHttpRequest 可以实现客户端与服务器只进行数据层面的交互,而不是视图层面的交互。


五种状态

XMLHttpRequest 对象的五种状态:


[*]0(未初始化)还没有调用send()方法
[*]1(载入)已调用send()方法,已创建服务器连接
[*]2(载入完成)send()方法执行完成,已经吸收到全部相应内容
[*]3(交互)正在剖析相应内容
[*]4(完成)相应内容剖析完成,可以在客户端调用了

三种属性

https://i-blog.csdnimg.cn/direct/a590e1ac67bf42f59b2671df52aef90f.png





ajax代码实现

ajax 通过 XMLHttpRequest 对象的 send() 方法来发送异步请求,通过 XMLHttpRequest 对象的onreadystatechange 函数来实现回调。
<html>
    <script>
    function getMessage(){

      // 实例化一个xmlHttpRequest对象
      var request = new XMLHttpRequest();
      
      // 设置xmlHttpRequest对象的回调函数
      /**
      * onreadystatechange 是request对象的一个属性,表示当准备状态发生改变时
      * 准备状态由 readystate 表示
      * 准备状态有 1、2、3、4 四个状态,如下:
      *   0 (UNSENT):请求已创建,但 open() 方法还未被调用。
      *   1 (OPENED):open() 方法已被调用,但 send() 方法还未被调用。
      *   2 (HEADERS_RECEIVED):send() 方法已被调用,响应头部和状态已可获得。
      *   3 (LOADING):响应主体正在加载,即响应内容(如果有的话)正在被接收。
      *   4 (DONE):请求已完成,响应已就绪。
      */
      // request.status 就是状态码
      request.onreadystatechange = function(){
            if(request.readyState == 4 && request.status == 200){
                // 接收响应结果,处理结果            
                  // eg: 使用request.responseText 接收后端响应回来的响应体中的数据
                        // 使用DOM编程
                  var inputEle = document.getElemenyById("message")
                  inputEle.value = request.responseText;
                  // eg: 把响应信息跳转到别的页面展示,使用BOM编程
                  window.location.href = "www.baidu.com"
            }      
      }
      
      // 设置发送请求的方式和请求的资源路径
      request.open("GET", "/user/checkUsernameUserd?username=zhangsan")
      
      // 发送请求
      request.send()
      
    }
    </script>
<body>
    <input type="text" iod="message">
</body>
</html>
ajax的缺点:回调地狱

所谓回调地狱就是回调函数嵌套的太多层了,看的不惬意而已。
即 AJAX 调用是异步的,这意味着它们不会阻塞代码的执行。但是当使用回调函数处置惩罚 AJAX 请求的效果时,假如多个请求依次依赖于前一个请求的效果,每个请求都需要等候前一个请求完成后才气执行,这就形成了嵌套的回调函数,即回调地狱。
var xhr1 = new XMLHttpRequest();
xhr1.open('GET', 'url1', true);

xhr1.onreadystatechange = function() {
if (xhr1.readyState === 4 && xhr1.status === 200) {
    // 处理第一个请求的结果
    var result1 = xhr.responseText;

    // 根据第一个请求的结果发起第二个请求
    var xhr2 = new XMLHttpRequest();
    xhr2.open('GET', 'url2?param=' + result1, true);

    xhr2.onreadystatechange = function() {
      if (xhr2.readyState === 4 && xhr2.status === 200) {
      // 处理第二个请求的结果
      var result2 = xhr2.responseText;

      // 根据第二个请求的结果发起第三个请求
      var xhr3 = new XMLHttpRequest();
      xhr3.open('GET', 'url3?param=' + result2, true);

      xhr3.onreadystatechange = function() {
          if (xhr3.readyState === 4 && xhr3.status === 200) {
            // 处理第三个请求的结果
            var result3 = xhr3.responseText;
            console.log(result3);
          }
      };
      xhr3.send();
      };
    };
    xhr2.send();
};
};
xhr.send(); 上述存在三个回调函数,分别是 xhr1.onreadystatechange = function() {}、xhr2.onreadystatechange = function() {}以及xhr3.onreadystatechange = function() {}。
此中,
xhr3.onreadystatechange = function() {} 嵌套在 xhr2.onreadystatechange = function() {} 中。
xhr2.onreadystatechange = function() {} 嵌套在 xhr1.onreadystatechange = function() {} 中。
看的不惬意,于是便出现了 promise。

promise

Promise 本质是个构造函数,其作用就是将 new Promist() 内的普通的函数转换成回调函数,而且返回一个 Promise 对象。我们可以从这个 Promise 对象身上获取异步操作的效果,即 promist 对象的状态。接着我们通过这个状态来执行相应的回调函数。
PromiseState(状态)

promise 对象代表一个异步操作,其有一个属性 PromiseState,该属性有三种状态:


[*]Pending(未决定的初始状态)
[*]Resolved(已完成,又称 Fulfilled)
[*]Rejected(被拒绝)
只有异步操作的效果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 promise 这个名字的由来,它的英语意思就是“答应",表现其他本领无法改变。
PromiseState 的初始值是 pending,而且状态只能改变一次。即promise 对象的状态改变,只有两种大概: 从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种环境发生,状态就擬固了,不会再变了,不停保持这个效果。
https://i-blog.csdnimg.cn/direct/2a81e79bc6d74e93b8c06f5ef16299b8.png
promise 的简单使用

这里为了方便理解,临时不讲 async 以及 await 关键字。
<script>
    let promise = new Promise(function (resolve, reject){
      resolve("hello promise")
      // 该方法表示 Promise 成功完成,并且将字符串 "hello promise" 作为结果
      // 传递给后续的 .then() 方法中的成功处理函数。
      
      // js 也可以像Java一样向外抛异常
      // throw new Eorror(`error message`)   
      // 在这写是用来测试promise2.catch()
    })
   
    let promise2 = promise.then(
      // 等待 promise 对象状态发生改变时才会执行的代码:
      function(value) { // resolve() 的实现代码在这
      
            // promise 的状态由 pending 转换为 resolved 时.会执行的函数
            console.log(value)    // 输出:hello promise
      },
      
      function(value ) {
             // promise 的状态由 pending 转换为 reject 时,会执行的函数
      }
    )
    let promise3 = promise2.catch(
      function() {
            // 当promise状态是reject 或 promise出现异常时 会执行的函数
      }   
    )
    promise3.finally(
      function() {
            // 当promise状态无论是resolved还是reject,都会执行的代码
      }   
    )
</script>
promise 封装 ajax

promise 的出现是为了解决 ajax 的回调地狱,那具体是怎样解决的呢?
书接上文,我们在讲 Ajax 回调地狱时举了一个例子,里面有三个回调函数相互嵌套。即


[*]xhr3.onreadystatechange = function() {} 嵌套在 xhr2.onreadystatechange = function() {} 中。
[*]xhr2.onreadystatechange = function() {} 嵌套在 xhr1.onreadystatechange = function() {} 中。
这里就专门针对这个例子来解说 promise 去怎样解决三个回调函数相互嵌套的。
// 创建一个Promise对象
function fetchData() {
    return new Promise((resolve, reject) => {
      var xhr1 = new XMLHttpRequest();
      xhr1.open('GET', 'https://api.example.com/data1', true);
      xhr1.onload = function() {
            if (xhr1.status === 200) {
                var data1 = JSON.parse(xhr1.responseText);
                // 请求成功,使用promise的resolve方法返回响应数据
                resolve(data1);
            } else {
                // 请求失败,使用promise的reject方法返回错误状态
                reject(new Error('请求失败'));
            }
      };
      xhr1.onerror = function() {
            // 请求失败,使用promise的reject方法返回错误状态
            reject(new Error('网络错误'));
      };
      xhr1.send();
    })
    .then(data1 => {
      return new Promise((resolve, reject) => {
            var xhr2 = new XMLHttpRequest();
            xhr2.open('GET', 'https://api.example.com/data2?param=' + data1.id, true);
            xhr2.onload = function() {
                if (xhr2.status === 200) {
                  var data2 = JSON.parse(xhr2.responseText);
                  resolve(data2);
                } else {
                  reject(new Error('请求失败'));
                }
            };
            xhr2.onerror = function() {
                reject(new Error('网络错误'));
            };
            xhr2.send();
      });
    }) // promise.then返回的就是一个promise对象,因此下一行可以再次.then
    .then(data2 => {
      return new Promise((resolve, reject) => {
            var xhr3 = new XMLHttpRequest();
            xhr3.open('GET', 'https://api.example.com/data3?param=' + data2.id, true);
            xhr3.onload = function() {
                if (xhr3.status === 200) {
                  var data3 = JSON.parse(xhr3.responseText);
                  resolve(data3);
                } else {
                  reject(new Error('请求失败'));
                }
            };
            xhr3.onerror = function() {
                reject(new Error('网络错误'));
            };
            xhr3.send();
      });
    })
    .then(data3 => {
      console.log('所有数据已获取:', data3);
    })
    .catch(error => {
      console.error('请求过程中发生错误:', error);
    });
}

// 调用函数
fetchData(); 可以看到 promise 接纳 promise对象.then() 的方式来解决嵌套的。实在作用都一样,只是 promise 的方式看起来更惬意而已。

axios

我们可以观察到 promise 封装 ajax 发送网络请求的代码包罗创建XMLHttpRequest对象、ajax发送请求、Ajax的回调函数、promise 的回调函数(promise.then()、promise.catch()、promise.finally)。这属实黑白常的贫苦。
因此人们发明了 axios,即 Axios 就是对 Ajax 的一种简化使用。



axios 的实现

eg:
向后端发送请求,获取土味情话并展示到页面。
https://i-blog.csdnimg.cn/direct/99ac04ac0c5a4098b95dce40b561f6da.png
<script setup>
import axios from 'axios'
import {reactive} from 'vue'
let message = reactive( {
    "code":1,
    "content":"我这么努力不是为了你,而是因为你"
})
function getLoveMessage() {
    // 使用 axios 发送请求获取土味情话:
    // axios() 函数可以发送各种类型的 HTTP 请求
    // 每个 axios() 请求都返回一个 Promise 对象,
    // 这意味着你可以使用 .then()、.catch() 方法来处理异步结果,或者使用 async/await 语法。
    let promise = axios({   
    // axios 中的参数是一个对象
    // 对象的第一个属性是请求方式
    // 对象的第二个属性是请求地址
    // 对象的第三个属性是请求体
    // 对象的第四个属性是请求参数
      method:"get",
      url:"https://api.uomg.com/api/rand.qinghua?format=json",
      
      // 如果请求方式是 get,则data中的数据会以键值对形式放在url后
      // 如果请求方式 post,则data中的数据会以JSON形式放入请求体
      data:{
            // 这里的数据会放入请求体 前提是请求方式得是 post。
            // 如果是 get,则请求体里什么也不会有。
            
      },
      params:{
            // 无论是post 还是 get,这里的数据都是以键值对方式将数据放
            // 入url后,注意:这里的数据和url需要用?隔开。
            // 在开发者工具=》网络=》标头=》请求标头中可以看到这里定义的数据。
            
      }
    })
   
    promise.then(
      function(response) {
            console.log(response)
            /*
            response就是响应结果对象,其中,
                data:服务端响应回来的数据
                status:响应状态码,例如,200
                statusText:响应状态描述,例如,200对应OK
                headers:本次响应的所有响应头
                config:本次请求的配置信息
                request:本次请求发送时所使用的 XMLHttpRequest 对象
            */
            console.log(response.data.code)
            console.log(response.data.content)
            /*前后端交互使用的是 json 字符串,但是当你使用 Ajax 或
            Axios时,则它们会自动地把JSON字符串转换成 JSON 对象。
            */
      }   
    ).catch{
      function(error) {
            console.log(error)
      }   
    }
}
</script>

<template>
    <div>
      <h1 v-text="message.content"></h1>
      <button @click="getLoveMessage()">变</button>
    </div>
</template>
至此我们就讲完了从AJAX到Promise再到Axios的发展之路,希望能对各位朋友理清 AJAX、Promise、Axios之间的关系起到帮助作用。

下面是扩展知识。
上述的 axios 代码还可以继续改进。
改进版1

上述代码中的 getLoveMessage() 中既有 axios 的发送请求,又有 promise.then 和 promist.catch,这就太耦合了。
那我现在把 getLoveMessage 中的 axios 的发送请求功能拆分到 getLoveWords() 中,可以看着更轻快些。
<script setup>
import axios from 'axios'
import {reactive} from 'vue'
let message = reactive( {
    "code":1,
    "content":"我这么努力不是为了你,而是因为你"
})

function getLoveWords() {
   return axios({    // axios 中的参数是一个对象
    method:"get",
    url:"https://api.uomg.com/api/rand.qinghua?format=json",
    data:{},
    params:{}
    })
}

function getLoveMessage() {
    let promise = getLoveWords()
    promise.then(
      function(response) {
            console.log(response)
            console.log(response.data.code)
      }   
    ).catch{}
}
</script>

<template>
    <div>
      <h1 v-text="message.content"></h1>
      <button @click="getLoveMessage()">变</button>
    </div>
</template>

改进版2

上述的改进版1中的promise可以使用 async 以及 await 关键字
<script setup>
import axios from 'axios'
import {reactive} from 'vue'
let message = reactive( {
    "code":1,
    "content":"我这么努力不是为了你,而是因为你"
})

function getLoveWords() {
   return axios({    // axios 中的参数是一个对象
    method:"get",
    url:"https://api.uomg.com/api/rand.qinghua?format=json",
    data:{},
    params:{}
    })
}

//function getLoveMessage() {
//    let promise = getLoveWords()
//    promise.then(
//      function(response) {
//            console.log(response)
//            console.log(response.data.code)
//      }   
//    ).catch{}
//}
// 上述代码不就是想要拿到 response 嘛,我们可以这么拿

async function getLoveMessage() {
    let response = await getLoveWords()
    Object.assign(message, response.data)   
    // assign()的作用是将后一个参数的属性赋值给前一个参数的同名属性上
}


</script>

<template>
    <div>
      <h1 v-text="message.content"></h1>
      <button @click="getLoveMessage()">变</button>
    </div>
</template>
改进版3

使用解构表达式
<script setup>
import axios from 'axios'
import {reactive} from 'vue'
let message = reactive( {
    "code":1,
    "content":"我这么努力不是为了你,而是因为你"
})

function getLoveWords() {
   return axios({    // axios 中的参数是一个对象
    method:"get",
    url:"https://api.uomg.com/api/rand.qinghua?format=json",
    data:{},
    params:{}
    })
}


//async function getLoveMessage() {
//    let response = await getLoveWords()
//    Object.assign(message, response.data)   
//    // assign()的作用是将后一个参数的属性赋值给前一个参数的同名属性上
//}
// 上述代码不就是想要拿到 response.data 嘛,我们可以使用解构表达式

async function getLoveMessage() {
    let {data} = await getLoveWords()
    Object.assign(message, data)   
}



</script>

<template>
    <div>
      <h1 v-text="message.content"></h1>
      <button @click="getLoveMessage()">变</button>
    </div>
</template>

改进版4

直接使用 axios.get 发送请求
<script setup>
import axios from 'axios'
import {reactive} from 'vue'
let message = reactive( {
    "code":1,
    "content":"我这么努力不是为了你,而是因为你"
})

function getLoveWords() {
//   return axios({    // axios 中的参数是一个对象
//    method:"get",
//    url:"https://api.uomg.com/api/rand.qinghua?format=json",
//    data:{},
//    params:{}
//    })
// 上述代码可以简写为
    return axios.get("https://api.uomg.com/api/rand.qinghua?format=json")
}

async function getLoveMessage() {
    let {data} = await getLoveWords()
    Object.assign(message, data)   
}



</script>

<template>
    <div>
      <h1 v-text="message.content"></h1>
      <button @click="getLoveMessage()">变</button>
    </div>
</template> 此中,
axios.get() 会自动发送 get 请求,并返回一个 promise 对象。此中的参数有以下几种情势:
https://i-blog.csdnimg.cn/direct/cfc8b24b8b964aa7aee4c43f4ade85e6.png
eg:
https://i-blog.csdnimg.cn/direct/cf215a2383df47f5bb6ae0181293fb4b.png


改进版4平行版

除了可以直接使用 axios.get 发送请求,我们也可以使用 axios.post 发送请求
<script setup>
import axios from 'axios'
import {reactive} from 'vue'
let message = reactive( {
    "code":1,
    "content":"我这么努力不是为了你,而是因为你"
})

function getLoveWords() {
    return axios.post("https://api.uomg.com/api/rand.qinghua?format=json")
}

async function getLoveMessage() {
    let {data} = await getLoveWords()
    Object.assign(message, data)   
}



</script>

<template>
    <div>
      <h1 v-text="message.content"></h1>
      <button @click="getLoveMessage()">变</button>
    </div>
</template> 此中,
axios.post() 会自动发送 post 请求,并返回一个 promise 对象。此中的参数有以下几种情势:
axios.post(url,{要放入请求体中的 JSON 串}, {请求的其他信息})
eg:
https://i-blog.csdnimg.cn/direct/4384f58580944bd0bb11737fe74a52f1.png
此中,
params 中的数据照样会放到 url 的?后。




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 从AJAX到Promise再到Axios