Promise、Ajax(XHR)、Fetch和Axios的区别 | 青训营笔记

227 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天

Promise、Ajax(XHR)、Fetch和Axios的区别

Ajax

Ajax是早期的JS中发送异步请求的方式,示例代码:

var xhr = new XMLHttpRequest()

xhr.onload = () => {
    console.log(JSON.parse(xhr.response))
    document.body.insertAdjacentHTML("beforeend", xhr.response)       
}

xhr.open("get","https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json")
xhr.send()

Ajax的异步处理方式是基于事件的,在创建xhr对象后,会为其onload事件(或onreadystatechange事件)绑定一个回调函数,事件会在服务器的响应数据返回后触发。这样当我们向服务器发送请求后,代码不会被阻塞,而是继续运行(期间不会影响到用户的操作)。直到服务器数据正常返回,onload事件被触发,回调函数才会执行。

回调函数的困扰

通过回调函数实现异步时,回调函数会在那些需要等待的操作完成后才执行。只要这样才能确保功能正常执行,这是因为回调函数的执行,通常会依赖操作完成后所产生的数据,比如在Ajax的回调函数中就需要编写处理响应数据的代码,响应数据回来之前函数是不能执行的。

如果是简单的回调还好,但是一旦遇到复杂的多次回调,事情也许就不那么美妙了。比如:有四个函数fn1()fn2()fn3()fn4(),四个函数互相依赖,2需要1的返回值,3需要2的返回值,4需要3的返回值。像是这样:

function fn1(num){
    return num + 1
}

function fn2(num){
    return num + 2
}

function fn3(num){
    return num + 3
}

function fn4(num){
    return num + 4
}

let result = fn1(10)
result = fn2(result)
result = fn3(result)
result = fn4(result)

几个函数并没有什么意义,单纯结构上的展示。修改为回调函数的版本,就是这样的:

function fn1(num, callback){
    return callback(num + 1)
}

function fn2(num, callback){
    return callback(num + 2)
}

function fn3(num, callback){
    return callback(num + 3)
}

function fn4(num, callback){
    return callback(num + 4)
}

fn1(10, n=>{
    fn2(n, n=>{
        fn3(n, n => {
            fn4(n, n => {
                console.log(n)
            })
        })
    })
})

这种代码结构被称为“回调地狱”或者“末日金字塔”。过多的回调增加了代码的复杂度,非常的不易于维护。可是异步本身又十分依赖于回调函数,所以要写好异步代码必须先处理好回调函数的问题。

Promise

回调函数的问题主要在于嵌套,回调函数之所以嵌套是因为当前的回调函数依赖于上一个回调函数的执行结果,为了确保在上一个回调函数执行完毕后当前回调函数才执行,我们只能在上一个回调函数中调用当前的回调函数。怎么能避免这个问题呢?Promise应运而生。

Promise直译过来为“承诺”。它可以用来存储异步代码(同步代码亦可),并且它可以确保无论异步代码执行成功或失败都会给我们返回一个结果。如此一来,我们便可以直接将函数的代码直接存储到Promise中,然后将Promise作为返回值返回,这样回调函数可以直接通过Promise来获取上一个函数的执行结果,并且Promise会确保只有得到结果时,才会调用回调函数。

示例:

const myPromise = new Promise((resolve) => {
    // 异步代码...
    resolve(10) // 返回运行结果
})

myPromise
    .then(result => console.log(result))
    .catch(e => console.log("代码出错", err))

首先,使用new Promise()来创建一个Promise对象,它需要一个回调函数作为参数,可以将异步的代码直接编写到回调函数中。回调函数的第一个参数是一个函数,这里命名为resolve,可以在异步功能完成后对其进行调用,然后将期望返回的结果设置为它的实参。

可以将Promise对象理解为存储异步代码结果的容器,异步代码的执行结果都存储到了该对象中,那么如何读取呢?可以直接调用promise对象的then方法,并传递一个回调函数作为参数,执行结果会通过回调函数的第一个参数返回。如果异步代码执行失败,then的回调函数不会执行,而是执行catch()方法的回调函数。

Fetch

Fetch是Ajax的进化版,直接使用了Fetch API,相较于Ajax更加简便:

fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json")
    .then(res => res.json())
    .then(data => {
        console.log(data)
        document.body.insertAdjacentHTML("beforeend", JSON.stringify(data))
    })

Axios

Axios是一个第三方的发送请求的工具,主要是用来代替的Ajax,同样支持Promise,且功能也较Ajax和Fetch要强大一些,只是使用时需要引入第三方库。

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<script>
  
    axios
        .get(
            "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json"
        )
        .then(function (response) {
            console.log(response)
        })
        .catch(function (error) {
            console.log(error)
        })
    
</script>

总结:

技术原生作用
Ajax发送请求
Fetch发送请求
Axios×发送请求
Promise为异步提供支持