JS: 用 Promise 写一个 axios
只要用过 Vue,没人会不知道 axios 这个库,他基乎取代了 jQuery 发 ajax 的功能了。今天我就用 Promise 来实现一个简单的 axios。
纯 JS 发 ajax
使用纯 JS 发 ajax 要 4 步。
- 创建 XMLHttpRequest 对象
- 使用
open
函数 - 设置
onreadystatechange
回调 - 使用
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 函数。
- 当 Promise 状态变成 fulfilled 时(成功),就调用 resolve
- 当 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。
正确的写法应该是 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)
})
}