饥人谷技术博客vue开发干货JavaScript 进阶营

JS: 用 Promise 写一个 axios

2019-02-05  本文已影响60人  写代码的海怪

只要用过 Vue,没人会不知道 axios 这个库,他基乎取代了 jQuery 发 ajax 的功能了。今天我就用 Promise 来实现一个简单的 axios。

纯 JS 发 ajax

使用纯 JS 发 ajax 要 4 步。

  1. 创建 XMLHttpRequest 对象
  2. 使用 open 函数
  3. 设置 onreadystatechange 回调
  4. 使用 send 传入请求数据
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onreadystatechange =  () => {
    if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(xhr.response)
    } else {
        reject(xhr.statusText)
    }
}
xhr.send(data)

上面的写法的对的,但是在 Promise 里是有坑的,下面会说到。

引入 Promise

Promise 传入参数为一个函数,而这个函数里面两个参数分别是 resolve 函数和 reject 函数。

  1. 当 Promise 状态变成 fulfilled 时(成功),就调用 resolve
  2. 当 Promise 状态变成 rejected 时(失败),就调用 reject

所以我们可能会写下在的代码:

new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open(method, url)
    xhr.onreadystatechange = () => {
       if (xhr.readyState === 4 && xhr.status === 200) {
            resolve(xhr.response)
        } else {
            reject(xhr.statusText)
        }
    }
    xhr.send(data)
})

注意,这里创建 Promise 对象的时候就会立刻,马上去执行 Promise 传入那个函数里的代码。但是 resolve 和 reject 还不会调用,现在 Promise 会进入 pending (等待)状态。

如果请求成功再去调用 resolve(此时 pending 变成 fulfilled),如果失败会去调用 reject(此时 pending 变成 rejected)。

就像我前面说的,这里有一个坑。现在的想法是希望 readyState 为 4(已经响应了)且 stats 为 200 (响应成功了)就去调用 resolve 函数。

首先我们要知道 Promise 的一个规则是:只要 Promise 状态一改变就不会再去改变了。但是 onreadystatechange 是会一直被调用的。

一直在判断 readyState 的值

他会一直查看 readyState 的值,直到 readyState === 4 为止才不会再去调用 onreadystatechange 的回调。如果现在 readyState === 2 时,按现在的代码会去调用 reject(xhr.statusText),Promise 的状态就会立马变成 rejected,状态一旦确定就不会再改变了,也就是说整个代码下来只调用了 reject。

调用了 reject 就不会再去调用 resolve

正确的写法应该是 readyState < 4 都不去调用,只有 4 时再去调用。

new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open(method, url)
    xhr.onreadystatechange =  () => {
        if (xhr.readyState !== 4) {
            return
        }
        if (xhr.status === 200) {
            resolve(xhr.response)
        } else {
            reject(xhr.statusText)
        }
    }
    xhr.send(data)
})

包装

解决这个坑后我们应该把上面的代码包装到一个函数里。

function axios(ajaxObj) {
    const {url, method, data} = ajaxObj

    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url)
        xhr.onreadystatechange =  () => {
            if (xhr.readyState !== 4) {
                return
            }
            if (xhr.status === 200) {
                resolve(xhr.response)
            } else {
                reject(xhr.statusText)
            }
        }
        xhr.send(data)
    })
}

使用的时候就可以用 .then() 来使用了。

function sendRequest() {
    const ajaxObj = {
        url: 'https://cnodejs.org/api/v1/topics',
        method: 'GET',
        data: ''
    }
    axios(ajaxObj)
        .then((response) => {
            console.log('resolve')
            console.log(response)
        })
        .catch((error) => {
            console.error(error)
        })
}
上一篇下一篇

猜你喜欢

热点阅读