fetch和XMLHttpRequest講解

來源:互聯網
上載者:User

標籤:

寫在前面

fetchXMLHttpRequest 非常類似,都是用來做網路請求。但是同複雜的XMLHttpRequest的API相比,fetch使用了Promise,這讓它使用起來更加簡潔,從而避免陷入”回調地獄”。

兩者比較

比如,如果我們想要實現這樣一個需求:請求一個URL地址,擷取響應資料並將資料轉換成JSON格式。使用fetchXMLHttpRequest實現的方式是不同的。

使用 XMLHttpRequest實現

使用XMLHttpRequest來實現改功能需要設定兩個監聽函數,分別用來處理成功和失敗的情況,同時還需要依次調用open()send()方法才能實現請求。

function reqListener() {    var data = JSON.parse(this.responseText);    console.log(data);}function reqError(err) {    console.log(‘Fetch Error : %S‘, err);}var oReq = new XMLHttpRequest();oReq.onload = reqListener;oReq.onerror = reqError;oReq.open(‘get‘, ‘./api/some.json‘, true);oReq.send();
使用 fetch實現

使用fetch來實現是這樣的:

fetch(‘./api/some.json‘)    .then(function(res) {        if (res.status !== 200) {            console.log(‘Looks like there was a problem. Status Code: ‘ + res.status);            return;        }        // 處理響應中的文本資訊         res.json().then(function(data) {            console.log(data);        });    })    .catch(function(err) {        console.log(‘Fetch Error : %S‘, err);    })

在將響應的文本資訊轉換成JSON格式前,需要先確保響應的狀態代碼為200。
fetch()請求後返回的響應是一個stream對象,這就意味著我們在調用json()方法後會返回一個Promise,因為讀取stream的過程是非同步作業的。

響應中的中繼資料

在上面的例子中,我們可以查看響應對象的狀態代碼,也知道了如何將響應轉換成JSON格式的資料。但其實我們可以訪問的中繼資料還有以下這些:

fetch("users.json").then(function(res) {    console.log(res.headers.get(‘Content-Type‘));    console.log(res.headers.get(‘Data‘));    console.log(res.status);    console.log(res.statusText);    console.log(res.type);    console.log(res.url);})
響應類型

當我們發送fetch請求時,返回的res.type可能是basiccorsopaque中的一種。這些類型可以告知我們資源從何而來,這樣就能知道該如何處理響應對象。

當我們請求的是同一域下的資源時,響應返回的類型為basic,此時沒有任何限制,我們可以查看響應中的任何資料。

如何請求的是跨域資源,那麼會返回一個CORS類型的頭部,並且響應類型為cors。這種類型跟上面的basic非常相似,只是它對回應標頭部的欄位訪問有限制,你只可以訪問這些屬性:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Opaque 類型的響應也是訪問跨域資源的時候產生的,只是回應標頭不是CORS類型的回應標頭而已。如果是這種類型的響應,那麼我們就不能讀取返回的資料,也不能查看請求的狀態代碼,這就是意味著我們將無法確定請求是成功了還是失敗了。目前在fetch()的實現中,無法請求跨域的資源。

我們可以為fetch請求定義mode屬性,來保證只有合格請求才會被處理。可以設定的mode屬性值如下:

  • same-origin 只有請求相同域下的資源才能成功,其他請求均被拒絕。
  • cors 允許請求同域或者跨域資源,但是跨域必須返回相應的跨域要求標頭部。
  • cors-with-forced-preflight 在發出實際請求前先做preflight檢查。
  • no-cors 針對跨域資源做請求,但是不返回CORS的回應標頭,這是屬於opaque類型的響應(window下無法使用)

在使用mode時,需要將fetch請求的第二個參數作為設定物件,並在其中配置具體的模式,如下代碼:

fetch("http://some-site.com/cors-enabled/some.json",{mode: ‘cors‘})    .then(function(res){        return res.text();    })    .then(function(text) {        console.log(‘Request successfully‘, text);    })    .catch(function(err) {        console.log(‘Request failed‘, error)    })
Promise 方法鏈

Promise 的特性之一就是可以實現鏈式調用,fetch也可以使用該特性,同時,使用鏈式調用可以讓請求的處理邏輯更加通用。

如果使用介面反饋的JSON格式資料,那麼針對每次響應,我們都需要檢查響應狀態並做JSON格式轉換。其實還能簡化代碼,那就是把狀態監測和JSON轉換的代碼放到單獨的函數中去。比如:

function status(res) {    if (res.status >= 200 && res.status < 300) {        return Promise.resolve(response)    } else {        return Promise.reject(new Error(res.statusText))    }}function json(res) {    return res.json()}fetch(‘user.json‘)    .then(status)    .then(json)    .then(function(data) {        console.log(‘Request succeeded with JSON response‘, data);    })    .catch(function(err) {        console.log(‘Request failed‘, error);    })

在上面的代碼中,我們定義了一個status方法來檢查response.status 的狀態,根據結果不同返回Promise.resolve()Promise.reject() ,這是fetch()方法鏈中的第一個方法調用。如果返回resolve狀態,我們會繼續調用json(),從而返回response.json()的執行結果。經過這些處理完我們已經可以擷取解析後的JSON對象。如果解析失敗,Promise返回reject狀態,執行catch裡的代碼進行錯誤處理。

這麼編碼更大的好處在於,你可以在所有的fetch請求中使用上面的邏輯代碼,從而讓代碼變得更加容易閱讀、維護和測試。

使用 fetch 請求發送憑證資訊

如果我們想在fetch請求中帶一些憑證資訊,比如cookie等,我們應該將請求中的credentials設定為include

fetch(url, {    credentials: ‘include‘})

注意: 伺服器返回 400,500 錯誤碼時並不會 reject,只有網路錯誤這些導致請求不能完成時,fetch 才會被 reject。

Response相關屬性及方法 bodyUsed
  1. 標記傳回值是否被使用過
  2. 這樣設計的目的是為了之後相容基於流的API,讓應用一次消費data,這樣就允許了JavaScript處理大檔案例如視頻,並且可以支援即時壓縮和編輯。
fetch(‘/test/content.json‘).then(function(res){    console.log(res.bodyUsed); // false    var data = res.json();    console.log(res.bodyUsed); //true    return data;}).then(function(data){    console.log(data);}).catch(function(error){    console.log(error);});
headers

返回Headers對象,該對象實現了Iterator,可通過for…of遍曆

fetch(‘/test/content.json‘).then(function(res){    var headers = res.headers;    console.log(headers.get(‘Content-Type‘)); // application/json    console.log(headers.has(‘Content-Type‘)); // true    console.log(headers.getAll(‘Content-Type‘)); // ["application/json"]    for(let key of headers.keys()){        console.log(key); // datelast-modified server accept-ranges etag content-length content-type    }    for(let value of headers.values()){        console.log(value);    }    headers.forEach(function(value, key, arr){        console.log(value); // 對應values()的傳回值        console.log(key); // 對應keys()的傳回值    });    return res.json();}).then(function(data){    console.log(data);}).catch(function(error){    console.log(error);});
ok
  1. 是否正常返回
  2. 代表狀態代碼在200-299之間
status

狀態代碼( 200表示 成功)

statusText

狀態原因 (‘OK’表示 成功)

type
  • basic:正常的,同域的請求,包含所有的headers。排除Set-CookieSet-Cookie2
  • corsResponse從一個合法的跨域請求獲得,一部分headerbody可讀。
  • error:網路錯誤。Responsestatus是0,Headers是空的並且不可寫。當Response是從Response.error()中得到時,就是這種類型。
  • opaqueResponse"no-cors"請求了跨域資源。依靠Server端來做限制。
url

返回完整的url字串。如:’http://xxx.com/xx?a=1’

arrayBuffer()

返回ArrayBuffer類型的資料的Promise對象

blob()

返回Blob類型的資料的Promise對象

clone()
  • 產生一個Response的複製
  • body只能被讀取一次,但clone方法就可以得到body的一個備份
  • 複製體仍然具有bodyUsed屬性,如果被使用過一次,依然會失效
fetch(‘/test/content.json‘).then(function(data){    var d = data.clone();    d.text().then(function(text){        console.log(JSON.parse(text));    });    return data.json();}).then(function(data){    console.log(data);}).catch(function(error){    console.log(error);});
json()

返回JSON類型的資料的Promise對象

text()

返回Text類型的資料的Promise對象

formData()

返回FormData類型的資料的Promise對象

fetch基本用法 get方式
fetch(‘/test/content.json‘).then(function(data){    return data.json();}).then(function(data){    console.log(data);}).catch(function(error){    console.log(error);});
post方式
fetch(‘/test/content.json‘, { // url: fetch事實標準中可以通過Request相關api進行設定    method: ‘POST‘,    mode: ‘same-origin‘, // same-origin|no-cors(預設)|cors    credentials: ‘include‘, // omit(預設,不帶cookie)|same-origin(同源帶cookie)|include(總是帶cookie)    headers: { // headers: fetch事實標準中可以通過Header相關api進行設定        ‘Content-Type‘: ‘application/x-www-form-urlencoded‘ // default: ‘application/json‘    },    body: ‘a=1&b=2‘ // body: fetch事實標準中可以通過Body相關api進行設定}).then(function(res){ res: fetch事實標準中可以通過Response相關api進行設定    return res.json();}).then(function(data){    console.log(data);}).catch(function(error){});
fetch不足之處無法監控讀取進度和插斷要求

Promises缺少了一些重要的XMLHttpRequest的使用情境。例如, 使用標準的ES6 Promise你無法收集進入資訊或插斷要求。而Fetch的狂熱開發人員更是試圖提供Promise API的擴充用於取消一個Promise。 這個提議有點自挖牆角的意思,因為將這將讓Promise變得不符合標準。但這個提議或許會導致未來出現一個可取消的Promise標準。 但另一方面,使用XMLHttpRequest你可以類比進度(監聽progress事件),也可以取消請求(使用abort()方法)。 但是,如果有必要你也可以使用Promise來包裹它

瀏覽器支援

目前Chrome 42+, Opera 29+, 和Firefox 39+都支援Fetch。微軟也考慮在未來的版本中支援Fetch。 諷刺的是,當IE瀏覽器終於微響應實現了progress事件的時候,XMLHttpRequest也走到了盡頭。 目前,如果你需要支援IE的話,你需要使用一個polyfill庫。

@參考 深入淺出Fetch API

fetch和XMLHttpRequest講解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.