Ajax Fetch 和 Axios(持續更新中...)

Ajax Fetch 和 Axios(持續更新中...)

知識點梳理

  1. AJAX 不是 JavaScript 的規範,它只是一個哥們「發明」的縮寫:Asynchronous JavaScript and XML,意思就是用 JavaScript 執行異步網絡請求。
  2. JavaScript 代碼都是單線程執行的,因爲這個「缺陷」,致使 JavaScript 的全部網絡操做,瀏覽器事件,都必須是異步執行。異步執行能夠用回調函數實現,回調函數很差看,不利於代碼複用,而鏈式寫法好處 邏輯統1、利於複用,因此出現 Primose Promise 有各類開源實現,在 ES6 中被統一規範,由瀏覽器直接支持。
  3. async await 是 Promise 語法糖,使異步的邏輯書寫標準的同步函數
  4. Fetch 是一個現代的概念, 等同於 XMLHttpRequest。它提供了許多與 XMLHttpRequest 相同的功能,但被設計成更具可擴展性和高效性。
  5. axios 是基於 Promise 的瀏覽器和 node.js http 客戶端
  6. Generator 是另一種全新的 javascript 異步解決方案,以後再說明

1. 原生 XHR

Ajax

AJAX 不是 JavaScript 的規範,它只是一個哥們「發明」的縮寫:Asynchronous JavaScript and XML,意思就是用 JavaScript 執行異步網絡請求。
屬性 描述
onreadystatechange 每當 readyState 屬性改變時,就會調用該函數。
readyState 存有 XMLHttpRequest 的狀態。從 0 到 4 發生變化。
0: 請求未初始化
1: 服務器鏈接已創建,open()方法已調用,可是 send()方法未調用
2: 請求已鏈接 send()方法已調用,HTTP 請求已發送到 Web 服務器。未接收到相應
3: 請求處理中
4: 請求已完成,且響應已就緒
status 200: "OK"
404: 未找到頁面
;(function() {
  var xmlhttp
  if (window.XMLHttpRequest) {
    //  IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執行代碼
    xmlhttp = new XMLHttpRequest()
  } else {
    // IE6, IE5 瀏覽器執行代碼
    xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
  }
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      console.log(xmlhttp.responseText)
    }
  }
  xmlhttp.open('GET', 'http://www.runoob.com/try/ajax/ajax_info.txt', true)
  xmlhttp.send()
})()

Ajax 安全限制

瀏覽器的同源策略致使的。默認狀況下,JavaScript 在發送 AJAX 請求時,URL 的域名必須和當前頁面徹底一致

跨域請求javascript

  1. 經過 Flash 插件發送 HTTP 請求,這種方式能夠繞過瀏覽器的安全限制,但必須安裝 Flash,而且跟 Flash 交互。不過 Flash 用起來麻煩,並且如今用得也愈來愈少了。
  2. 經過在同源域名下架設一個代理服務器來轉發,JavaScript 負責把請求發送到代理服務器
  3. JSONP 它有個限制,只能用 GET 請求,而且要求返回 JavaScript。這種方式跨域其實是利用了瀏覽器容許跨域引用 JavaScript 資源
  4. CORS CORS 全稱 Cross-Origin Resource Sharing,是 HTML5 規範定義的如何跨域訪問資源。面這種跨域請求,稱之爲「簡單請求」。簡單請求包括 GET、HEAD 和 POST(POST 的 Content-Type 類型僅限 application/x-www-form-urlencoded、multipart/form-data 和 text/plain),而且不能出現任何自定義頭(例如,X-Custom: 12345),一般能知足 90%的需求

2.Promise

在 JavaScript 的世界中,全部代碼都是單線程執行的。java

因爲這個「缺陷」,致使 JavaScript 的全部網絡操做,瀏覽器事件,都必須是異步執行。異步執行能夠用回調函數實現node

function callback() {
  console.log('Done')
}
console.log('before setTimeout()')
setTimeout(callback, 1000) // 1秒鐘後調用callback函數
console.log('after setTimeout()')

鏈式寫法的好處在於,先統一執行 AJAX 邏輯,不關心如何處理結果,而後,根據結果是成功仍是失敗,在未來的某個時候調用 success 函數或 fail 函數。jquery

古人云:「君子一言既出;駟馬難追」,這種「承諾未來會執行」的對象在 JavaScript 中稱爲 Promise 對象。ios

使用 Promise 封裝 ajax 簡化異步處理

// ajax函數將返回Promise對象:
function ajax(method, url, data) {
  var request = new XMLHttpRequest()
  return new Promise(function(resolve, reject) {
    request.onreadystatechange = function() {
      if (request.readyState === 4) {
        if (request.status === 200) {
          resolve(request.responseText)
        } else {
          reject(request.status)
        }
      }
    }
    request.open(method, url)
    request.send(data)
  })
}

var p = ajax('GET', '/api/categories')
p.then(function(text) {
  // 若是AJAX成功,得到響應內容
}).catch(function(status) {
  // 若是AJAX失敗,得到響應代碼
})

Promise 使用方法

;(function() {
  console.time('doIt')
  const time1 = 300
  step1(time1)
    .then(time2 => step2(time2))
    .then(time3 => step3(time3))
    .then(result => {
      console.log(`result is ${result}`)
      console.timeEnd('doIt')
    })
})()

function takeLongTime(n) {
  return new Promise(resolve => {
    setTimeout(() => resolve(n + 200), n)
  })
}
function step1(n) {
  console.log(`step1 with ${n}`)
  return takeLongTime(n)
}
function step2(n) {
  console.log(`step2 with ${n}`)
  return takeLongTime(n)
}
function step3(n) {
  console.log(`step3 with ${n}`)
  return takeLongTime(n)
}

Promise.all()

兩個任務是能夠並行執行的,用 Promise.all()實現
var p1 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 500, 'P1')
})
var p2 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 6000, 'P2')
})
// 同時執行p1和p2,並在它們都完成後執行then:
Promise.all([p1, p2]).then(function(results) {
  console.log(results) // 得到一個Array: ['P1', 'P2']
})

Promise.race()

有些時候,多個異步任務是爲了容錯,只須要得到先返回的結果便可
var p1 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 3000, 'P1')
})
var p2 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 6000, 'P2')
})
// 同時執行p1和p2,其中一個完成後執行then:
Promise.race([p1, p2]).then(function(results) {
  console.log(results) //  // 'P1'
})

3.async await

await

await 操做符用於等待一個 Promise 對象。它只能在異步函數 async function 中使用

await 表達式會暫停當前 async function 的執行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回調的 resolve 函數參數做爲 await 表達式的值,繼續執行 async function。
若 Promise 處理異常(rejected),await 表達式會把 Promise 的異常緣由拋出。es6

另外,若是 await 操做符後的表達式的值不是一個 Promise,則返回該值自己。ajax

// 若是一個 Promise 被傳遞給一個 await 操做符,await 將等待 Promise 正常處理完成並返回其處理結果。
function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x)
    }, 2000)
  })
}

async function f1() {
  var x = await resolveAfter2Seconds(10)
  console.log(x) // 10
}
f1()
// 若是 Promise 處理異常,則異常值被拋出。
async function f3() {
  try {
    var z = await Promise.reject(30)
  } catch (e) {
    console.log(e) // 30
  }
}
f3()

async

async function 聲明用於定義一個返回 AsyncFunction 對象的異步函數。異步函數是指經過事件循環異步執行的函數,它會經過一個隱式的 Promise 返回其結果。

一個返回的 Promise 對象會以 async function 的返回值進行解析(resolved),或者以該函數拋出的異常進行回絕(rejected)。npm

async 函數中可能會有 await 表達式,這會使 async 函數暫停執行,等待 Promise 的結果出來,而後恢復 async 函數的執行並返回解析值(resolved)。json

function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved')
    }, 2000)
  })
}

async function asyncCall() {
  console.log('calling')
  var result = await resolveAfter2Seconds()
  console.log(result)
  // expected output: 'resolved'
}

asyncCall()

4.Fetch

Fetch 是一個現代的概念, 等同於 XMLHttpRequest。它提供了許多與 XMLHttpRequest 相同的功能,但被設計成更具可擴展性和高效性。

Fetch 是掛在在 window 下的axios

Fetch 的核心在於對 HTTP 接口的抽象,包括 Request,Response,Headers,Body,以及用於初始化異步請求的 global fetch。Fetch 還利用到了請求的異步特性——它是基於 Promise 的。

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json()
  })
  .then(function(myJson) {
    console.log(myJson)
  })
postData('http://example.com/answer', { answer: 42 })
  .then(data => console.log(data)) // JSON from `response.json()` call
  .catch(error => console.error(error))

function postData(url, data) {
  // Default options are marked with *
  return fetch(url, {
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, same-origin, *omit
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer' // *client, no-referrer
  }).then(response => response.json()) // parses response to JSON
}

5. axios

axios 是一個基於 Promise 用於瀏覽器和 nodejs 的 HTTP 客戶端
axios
  .get('/user', { params: { ID: 12345 } })
  .then(function(response) {})
  .catch(function(error) {})

能夠經過將相關配置傳遞給 axios 來進行請求。

axios({
  method: 'post',
  url: '/user/12345',
  data: { firstName: 'Fred', lastName: 'Flintstone' }
})

請求方法別名

axios.request(config)
axios.get(url [,config])
axios.delete(url [,config])
axios.head(url [,config])
axios.post(url [,data [,config]])
axios.put(url [,data [,config]])
axios.patch(url [,data [,config]])

Request Config 請求設置

{
    //`url`是服務器連接,用來請求
    url:'/user',

    //`method`是發起請求時的請求方法
    method:`get`,

    //`baseURL`若是`url`不是絕對地址,那麼將會加在其前面。
    //當axios使用相對地址時這個設置很是方便
    //在其實例中的方法
    baseURL:'http://some-domain.com/api/',

    //`transformRequest`容許請求的數據在傳到服務器以前進行轉化。
    //這個只適用於`PUT`,`GET`,`PATCH`方法。
    //數組中的最後一個函數必須返回一個字符串或者一個`ArrayBuffer`,或者`Stream`,`Buffer`實例,`ArrayBuffer`,`FormData`
    transformRequest:[function(data){
        //依本身的需求對請求數據進行處理
        return data;
    }],

    //`transformResponse`容許返回的數據傳入then/catch以前進行處理
    transformResponse:[function(data){
        //依須要對數據進行處理
        return data;
    }],

    //`headers`是自定義的要被髮送的頭信息
    headers:{'X-Requested-with':'XMLHttpRequest'},

    //`params`是請求鏈接中的請求參數,必須是一個純對象,或者URLSearchParams對象
    params:{
        ID:12345
    },

    //`paramsSerializer`是一個可選的函數,是用來序列化參數
    //例如:(https://ww.npmjs.com/package/qs,http://api.jquery.com/jquery.param/)
    paramsSerializer: function(params){
        return Qs.stringify(params,{arrayFormat:'brackets'})
    },

    //`data`是請求體須要設置的數據
    //只適用於應用的'PUT','POST','PATCH',請求方法
    //當沒有設置`transformRequest`時,必須是如下其中之一的類型(不可重複?):
    //-string,plain object,ArrayBuffer,ArrayBufferView,URLSearchParams
    //-僅瀏覽器:FormData,File,Blob
    //-僅Node:Stream
    data:{
        firstName:'fred'
    },
    //`timeout`定義請求的時間,單位是毫秒。
    //若是請求的時間超過這個設定時間,請求將會中止。
    timeout:1000,

    //`withCredentials`代表是否跨網站訪問協議,
    //應該使用證書
    withCredentials:false //默認值

    //`adapter`適配器,容許自定義處理請求,這會使測試更簡單。
    //返回一個promise,而且提供驗證返回(查看[response docs](#response-api))
    adapter:function(config){
        /*...*/
    },

    //`auth`代表HTTP基礎的認證應該被使用,而且提供證書。
    //這個會設置一個`authorization` 頭(header),而且覆蓋你在header設置的Authorization頭信息。
    auth:{
        username:'janedoe',
        password:'s00pers3cret'
    },

    //`responsetype`代表服務器返回的數據類型,這些類型的設置應該是
    //'arraybuffer','blob','document','json','text',stream'
    responsetype:'json',

    //`xsrfHeaderName` 是http頭(header)的名字,而且該頭攜帶xsrf的值
    xrsfHeadername:'X-XSRF-TOKEN',//默認值

    //`onUploadProgress`容許處理上傳過程的事件
    onUploadProgress: function(progressEvent){
        //本地過程事件發生時想作的事
    },

    //`onDownloadProgress`容許處理下載過程的事件
    onDownloadProgress: function(progressEvent){
        //下載過程當中想作的事
    },

    //`maxContentLength` 定義http返回內容的最大容量
    maxContentLength: 2000,

    //`validateStatus` 定義promise的resolve和reject。
    //http返回狀態碼,若是`validateStatus`返回true(或者設置成null/undefined),promise將會接受;其餘的promise將會拒絕。
    validateStatus: function(status){
        return status >= 200 && stauts < 300;//默認
    },

    //`httpAgent` 和 `httpsAgent`當產生一個http或者https請求時分別定義一個自定義的代理,在nodejs中。
    //這個容許設置一些選選個,像是`keepAlive`--這個在默認中是沒有開啓的。
    httpAgent: new http.Agent({keepAlive:treu}),
    httpsAgent: new https.Agent({keepAlive:true}),

    //`proxy`定義服務器的主機名字和端口號。
    //`auth`代表HTTP基本認證應該跟`proxy`相鏈接,而且提供證書。
    //這個將設置一個'Proxy-Authorization'頭(header),覆蓋原先自定義的。
    proxy:{
        host:127.0.0.1,
        port:9000,
        auth:{
            username:'cdd',
            password:'123456'
        }
    },

    //`cancelTaken` 定義一個取消,可以用來取消請求
    //(查看 下面的Cancellation 的詳細部分)
    cancelToken: new CancelToken(function(cancel){
    })
}

返回響應概要 Response Schema

{
    //`data`是服務器的提供的回覆(相對於請求)
    data{},

    //`status`是服務器返回的http狀態碼
    status:200,


    //`statusText`是服務器返回的http狀態信息
    statusText: 'ok',

    //`headers`是服務器返回中攜帶的headers
    headers:{},

    //`config`是對axios進行的設置,目的是爲了請求(request)
    config:{}
}

全局默認設置

axios.defaults.baseURL = 'https://api.example.com'
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN
axios.defaults.headers.post['Content-Type'] =
  'application/x-www-form-urlencoded'

攔截器 interceptors

你能夠在請求或者返回被 then 或者 catch 處理以前對他們進行攔截。

//添加一個請求攔截器
axios.interceptors.request.use(
  function(config) {
    //在請求發送以前作一些事
    return config
  },
  function(error) {
    //當出現請求錯誤是作一些事
    return Promise.reject(error)
  }
)

//添加一個返回攔截器
axios.interceptors.response.use(
  function(response) {
    //對返回的數據進行一些處理
    return response
  },
  function(error) {
    //對返回的錯誤進行一些處理
    return Promise.reject(error)
  }
)

fetch axios封裝

項目中使用fetch 和axios 時,經常會作一些底層的封裝,好比一些統一的參數處理,異常處理,HTTP >= 300的處理
下一遍將嘗試對axios、fetch的封裝

參考

  1. AJAX
  2. Promise
  3. async await 語法描述
  4. Fetch 語法描述
  5. ECMAScript 6 入門
  6. 翻譯比較到位的 axios 中文文檔
相關文章
相關標籤/搜索