从AJAX到Promise再到Axios

ajax

 

异步交互

首先我先讲讲为什么需要 ajax,因为我们需要异步交互,那我们为什么需要异步交互呢?

我认为异步交互有两个好处:

1. 相比于同步交互,异步交互可以更大程度地提高服务器的使用效率,不至于让服务器有空闲的时候。

拓展一个知识点: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(完成)响应内容解析完成,可以在客户端调用了

 

三种属性

 

 

 

 

 

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。只要这两种情况发生,状态就擬固了,不会再变了,一直保持这个结果。

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:

向后端发送请求,获取土味情话并展示到页面。

<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 对象。其中的参数有以下几种形式:

eg:

 

 

改进版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:

其中,

params 中的数据照样会放到 url 的?后。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值