異步請求與Fetch

再談異步請求

  • Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。vue

  • 在瀏覽器,耗時很長的操做都應該異步執行,避免瀏覽器失去響應,最好的例子就是Ajax操做。經過XHRHttpRequest對象及時監聽完成事件,執行事件回調函數不會堵塞程序運行。jquery

關於Fetch

Fetch API 提供了一個 JavaScript接口,用於訪問和操縱HTTP管道的部分,例如請求和響應。它還提供了一個全局 fetch()方法,該方法提供了一種簡單,合乎邏輯的方式來跨網絡異步獲取資源。這種功能之前是使用 XMLHttpRequest實現的。Fetch提供了一個更好的替代方法,能夠很容易地被其餘技術使用,例如 Service Workers。Fetch還提供了單個邏輯位置來定義其餘HTTP相關概念,例如 CORS和HTTP的擴展。web

  • 新的Fetch API是XHRHttpRequest異步請求的另外一種方案,比起其複雜糅雜的寫法,fetch能更簡潔的獲取到數據。ajax

  • XHRHttpRequest使用事件回調函數容易進入回調地獄,而Fetch Api接收url參數返回Promise對象。json

  • Fetch 提供了對 Request 和 Response (以及其餘與網絡請求有關的)對象的通用定義。使之從此能夠被使用到更多地應用場景中api

Fetch的簡單使用

let url = 'http://jsonplaceholder.typicode.com/users'
fetch(url).then((res)=>{
  console.log(res)
}).catch((err)=>{})
  • 將url傳遞給fetch時會當即返回一個promise對象此時的狀態是pending,當promise被經過時會返回一個response對象。若是咱們用習慣了好比jquery封裝好的ajax方法,很容易會覺得上面的res就是返回的data。跨域

  • fetch 只有在遇到網絡錯誤的時候纔會 reject 這個 promise,好比用戶斷網或請求地址的域名沒法解析等。只要服務器可以返回 HTTP 響應(甚至只是 CORS preflight 的 OPTIONS 響應),promise 必定是 resolved 的狀態。promise

  • fetch返回Promise對象
    圖片描述瀏覽器

Response簡介

clipboard.png

  • response.type有如下幾種緩存

    • basic 標準值,同源響應

    • cors 收到一個有效的跨域請求

    • opaque 跨域請求但服務器沒有返回cors響應頭

  • 可是無效的跨域請求若是像下面這麼寫,其實根本沒法拿到response對象
    clipboard.png

  • request.mode用於肯定跨域請求是否致使有效的響應

    • same-origin 同源狀況下才可請求成功,不然拋出錯誤

      • 好比在有效的跨域請求設置這個模式會提示
        Fetch API cannot load http://jsonplaceholder.typico... Request mode is "same-origin" but the URL's origin is not same as the request origin file://.

    • cors: 表示同域和帶有CORS響應頭的跨域下可請求成功.

    • cors-with-forced-preflight: 表示在發出請求前, 將執行preflight檢查.

    • no-cors: 用於跨域相應不帶cors的狀況,此時相應類型爲opaque

  • 根據上面的錯誤修改fetch的模式就能夠返回response對象了,能夠對比發現無效跨域請求並無返回什麼有價值的信息例如url,status,statusText等

    clipboard.png

  • reponse.body屬於ReadableStream類型

    • 當讀取CSV等大文件時經過流來讀取,能夠選擇在讀取到要獲取的數據後中止流,而不是獲取到所有響應數據再去查找。

    • 對於經常使用返回的幾種數據類型,可使用blob()、text()、formData()、json(),這幾種方法都會講body標記爲已讀數據,因此想再次獲取的時候就會報錯

    • 好比下面的代碼,真實返回的數據不是json類型的,想利用catch方法去捕獲而後再次返回text類型,瀏覽器時會報TypeError: Already read

    fetch(url).then(function(response) {
      return response.json().catch(function() {
        return response.text();
      });
    });
  • 正確的方式是使用clone()方法先對數據進行拷貝,響應數據經過clone並不會被回收,一直留在內存中,直至被讀取

    fetch(url).then(function(response) {
      return response.clone().json().catch(function() {
        return response.text();
      });
    });

Request簡介

clipboard.png

  • method:設置請求方法,默認GET方法

  • credentials fetch方法默認不發送cookies,若是遇到401 Unauthorized沒有權限問題就要看看是否有設置credentials

    • omit: 從不發送cookies.

    • same-origin: 只有當URL與響應腳本同源才發送cookies.

    • include: 老是發送cookies, 即便來自跨域的請求

    clipboard.png 默認狀況下請求不帶coookie

  • cache 設置緩存,跟XHR對比在請求時就能夠控制緩存,雖然這是有爭議的,由於這暴露了用戶歷史記錄

    • default

      • 若是是最新的資源,則返回緩存

      • 若是資源已過時則向服務器發出條件請求;若是服務器指示資源沒有改變則返回緩存;不然從服務器中下載,並更新緩存。

      • 若是沒有找到緩存則發出正常情求,但不緩存資源。

    • no-store 直接從服務器獲取資源而且不會將資源緩存

    • reload 直接從服務器獲取資源,而且緩存資源

    • no-cache

      • 若是資源有效或者過時,瀏覽器會發出一個條件請求,若是服務器返回沒有改變,則返回緩存,不然,從新獲取資源並更新緩存

      • 若是不存在資源則瀏覽器發出正常請求下載並更新緩存

    • force-cache

      • 若是有緩存直接返回緩存,不然下載更新資源

    • only-if-cached

      • 若是有緩存直接返回緩存,不然瀏覽器返回錯誤

XHR和Fetch其餘的對比

  • fetch沒有xhr中的abort()方法來中斷請求

  • fetch沒有progress進度事件監聽數據,可是你能夠經過獲取content-length計算進度

  • XHR open方法有最後一個參數設爲false的時候是同步請求,fetch沒有提供同步請求的方法,但同步請求並不經常使用

  • fetch 並無像XHR的timeout屬性來設置延時

Fetch實踐體驗

  • 習慣了像是vue-resource等插件封裝好的get,post方法,在使用fetch的時候很容易會被它看似簡潔的api給迷惑

  1. fetch兼容性問題

    • 咱們須要引入fetch 的polyfill,扒開源碼咱們能夠發現實現原理仍是創建在XMLHttpRequest上的,可是因爲polyfill要實現fetch如Blob類文件對象等,在safari和ie上仍是有兼容性問題的

  2. 參數傳遞問題

    • 在get請求方式中,咱們仍是隻能像xhr那樣以拼接url字符串的方式來傳遞,這與fetch看起來簡潔的api有點不符。

      • 雖然原生的URLSearchParams 接口定義了用來處理 URL 參數串的方法。可是這個api目前兼容性也是不怎麼好

      var paramsString = "q=URLUtils.searchParams&topic=api"
      var searchParams = new URLSearchParams(paramsString);
      searchParams.append("topic", "webdev");
      searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"

    • 在post請求中,數據要以body字段來傳遞參數,也就是說咱們要經常使用的json格式要通過下面格式的轉換,就像XMLHttpRequest那樣

      body 能夠是如下任何一種類型的實例:

      ArrayBuffer
      ArrayBufferView (Uint8Array and friends)
      Blob/File
      string
      URLSearchParams
      FormData
  3. 響應數據的問題

    • 在回調函數中咱們首先接受到的是response對象,因爲body是ReadableStream類型,幾乎很難預先判斷數據是什麼類型,也就很難判斷是到底用text()、json()方法解析數據

    • response對象有一個bodyUsed的屬性初始是false,調用text()、json()等方法會讀取Response對象而且將它設置爲已讀,這時bodyUsed爲true

      clipboard.png bodyUsed初始值爲false

- 解決辦法有兩種但都有必定的不足
     1.是經過clone加catch方式找到正確的輸出方式,可是要注意調用方法的順序,否則一個json有可能最後仍是以text的方式解析,還要注意的是最後要將response的數據回收或者讀取,由於clone方法的讀取並不影響原來的reponse對象
fetch(url).then(function(response) {
      return response.json().catch(function() {
         return response.text();
      });
});
2.是經過判斷content-type的方式,若是已知的返回格式很少的狀況下用仍是不錯的選擇
fetch(url).then(function(response) {
      if (response.headers.get('Content-Type') === 'application/json') {
        return response.json();
      }
      return response.text();
});

小結

  • 基於promise的fecth方法可讓咱們遠離回調地獄而不須要其餘的封裝。

  • fetch 將 response.body 設計成 ReadableStream 實際上是很是有前瞻性的,這種設計讓你在請求大致積文件時變得很是有用。
    clipboard.png

參考資料:https://jakearchibald.com/201...

相關文章
相關標籤/搜索