引: axios在項目中常用,但有次面試讓我把ajax封裝成axios形式,當時竟然沒寫出來。看來仍是不能停留在使用的表層。 回來研究promise以爲也並非很難,關鍵是掌握Promise A+規範以及Promise寫法。
先看看axios如何實現的。javascript
axios的自我介紹:Promise based HTTP client for the browser and node.js
即:
我,axios,就是基於Promise,服務於瀏覽器和node.js的的HTTP客戶端。java
下面重點看我想了解的ajax部分。node
The modules under adapters/ are modules that handle dispatching a request and settling a returned Promise once a response is received.
此模塊主要處理分發請求,並在返回的Promise一旦有響應被接收的狀況下進行處理。ios
源碼中查看:axios/lib/adapters/xhr.js
有這樣一段讓我眼前一亮:git
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
})
}
複製代碼
找到promise大本營了!繼續看源碼,接下來就是Promise內部處理ajax重要的 open
, onreadystatechange
, send
幾處實現,照樣循規蹈矩,挑出來看:github
// =>...omit config...
// step1=>
var request = new XMLHttpRequest();
// step2=>
request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
// step3=>
request.onreadystatechange = function handleLoad() {
if (!request || request.readyState !== 4) {
return;
}
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
// Prepare the response
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
settle(resolve, reject, response);
// Clean up request
request = null;
};
// =>...omit handle process...
// step4=>
request.send(requestData);
複製代碼
以上只是ajax的實現,但核心的then鏈式調用並無實現,真正的實現來看axios/lib/core/settle.js
下的settle方法:面試
/** * Resolve or reject a Promise based on response status. * * @param {Function} resolve A function that resolves the promise. * @param {Function} reject A function that rejects the promise. * @param {object} response The response. */
module.exports = function settle(resolve, reject, response) {
var validateStatus = response.config.validateStatus;
if (!validateStatus || validateStatus(response.status)) {
resolve(response);
} else {
reject(createError(
'Request failed with status code ' + response.status,
response.config,
null,
response.request,
response
));
}
};
複製代碼
關鍵的resolve
和reject
解決後,按照規定(Promise A+ 規範 )ajax
promise必須提供then方法來存取它當前或最終的值或者緣由。json
此時能夠根據promise.then(onFulfilled, onRejected)
,利用其中的兩個方法對返回作處理。 以上已經基本實現了ajax的功能。axios
下面仿照axios的思想封裝本身的promise_ajax
function pajax({ url= null, method = 'GET', dataType = 'JSON', async = true}){
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open(method, url, async)
xhr.responseType = dataType
xhr.onreadystatechange = () => {
if(!/^[23]\d{2}$/.test(xhr.status)) return
if(xhr.readyState === 4) {
let result = xhr.responseText
resolve(result)
}
}
xhr.onerror = (err) => {
reject(err)
}
xhr.send()
})
}
複製代碼
默認調用JSON格式並測試成功
ajax({
url:'./test.json',
method: 'get'
}).then((result)=>{
console.log(result)
},(err)=>{
})
複製代碼
針對不一樣請求類型,文件類型,作不一樣的處理
// 判斷請求類型是否爲GET
let isGet = /^(GET|DELETE|HEAD)$/i.test(method)
// 判斷url有沒有?,有的話就添加&
let symbol = url.indexOf('?')>-1 ? '&' : '?'
// GET系列請求才處理cache
if(isGet){
!cache ? url+= `${symbol}_${Math.random()}`: null
}
複製代碼
let result = xhr.responseText
// 根據dataType即不一樣的文件類型,對返回的內容作處理
switch(this.dataType.toUpperCase()){
case 'TEXT':
case 'HTML':
break;
case 'JSON':
result = JSON.parse(result)
break;
case 'XML':
result = xhr.responseXML
}
複製代碼
function formatData(data){
if(Object.prototype.toString.call(data)==='[object Object]'){
let obj = data
let str = ''
for(let key in obj){
if(obj.hasOwnProperty(key)){
str+=`${key}=${obj[key]}&`
}
}
// 去掉最後多加的&
str = str.replace(/&$/g,'')
return str
}
}
if(data !== null){
data = formatData(data)
if(isGet){
url += symbol + data
data = null
}
}
複製代碼
簡單測試結果:
pajax({
url:'./test.json',
method: 'get',
cache: false,
data:{
name:'jyn',
age:20
}
}).then((result)=>{
console.log(result)
},(err)=>{
console.log(err)
})
複製代碼
運行結果爲: Request URL: http://localhost:63342/june/HTTP_AJAX/test.json?_0.6717612341262227?name=jyn&age=20
目前靠譜,且then
方法也能獲取到文件數據。 cache設置爲false時,每次刷新獲取都是200,不走304,功能正常。
以上,基於Promise的簡易ajax就完工大吉啦!
Author: Yanni Jia Nickname: 很是兔 Reference: axios源碼:https://github.com/axios/axios