XMLHttpRequest是一個設計粗糙的API,不符合關注分離(Separation of Concerns)的原則,配置和調用方式很是混亂,並且基於事件的異步模型寫起來也沒有現代的Promise,generator/yield,async/await友好。es6
Fetch的出現就是爲了解決XHR的問題。
傳統使用XHR發送一個json請求通常是這樣ajax
var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'json'; xhr.onload = function () { console.log(xhr.response); } xhr.onerror = function () { console.log('Oops, error'); } xhr.send();
使用Fetch後json
fetch(url).then(function (response) { return response.json(); }).then(function (data) { console.log(data); }).catch(function (e) { console.log('Oops, error') })
使用ES6的箭頭函數後:跨域
fetch(url).then(response => response.json()) .then(data => console.log(data)) .catch(e => console.log('Oops, error', e))
但這種Promise的寫法仍是有callback的影子,並且promise使用catch方法進行錯誤處理有點奇怪。
使用async/await進行優化(屬於ES7)數組
try { let response = await fetch(url); let data = response.json(); console.log(data); } catch (e) { console.log('Oops, error', e); } // 注:這段代碼若是想運行,外面須要包一個async function
使用await後,寫異步代碼就像寫同步代碼同樣。await後面能夠跟Promise對象,表示等待Promise的resolve()才繼續向下執行,若是Promise被reject()或拋出異常則被外面的try...catch捕獲promise
Fetch優勢:
1. 語法簡潔,更加語義化
2. 基於標準Promise實現,支持 async/await
3. 同構方便,使用 isomorphic-fetch瀏覽器
Fetch polyfill的基本原理是探測是否存在 window.fetch 方法,若是沒有則用XHR實現。有些瀏覽器(Chrome 45)原生支持Fetch,但響應中有中文時會亂碼。(可以使用fetch-detector和fetch-ie8)服務器
fetch(url, {credentials: 'include'})
由於fetch返回promise致使碼,在某些錯誤的http狀態下如400、500等不會reject,相反會被resolve;只有網絡錯誤會致使請求不能完成時,fetch纔會被reject;因此通常會對fetch請求作一層封裝,以下:cookie
function checkStatus (response) { if ( response.status >= 200 && response.status < 300) { return response; } const error = new Error (response.statusText); error.response = response; throw error; } function parseJSON (response) { return response.json(); } export default function request (url, options) { let opt = options || {}; return fetch (url, {credentials : 'include', ...opt}) .then (checkStatus) .then (parseJSON) .then (data => data) .catch (err => err) }
fetch不像大多數ajax庫能夠對請求設置超時timeout,因此在fetch標準添加超時feature以前,都須要polyfill該特性。
咱們真正須要的是abort(), timeout能夠經過timeout+abort方式實現,起到真正超時丟棄當前的請求。網絡
實現fetch的timeout功能,思想就是新建立一個能夠手動控制promise狀態的實例,對於不一樣狀態來對新建立的promise進行resolve或者reject,從而達到實現timeout功能。
var oldFetchfn = fetch; // 攔截原始的fetch方法 window.fetch = function (input, opts) { return new Promise ( function (resolve, reject) { var timeoutId = setTimeout (function () { reject (new Error ('fetch timeout')) }, opts.timeout); oldFetchfn (input, opts).then( res => { clearTimeout (timeoutId); resolve (res); }, err => { clearTimeout (timeoutId); reject (err); } ) }) }
模擬XHR的abort功能:
var oldFetchfn = fetch; window.fetch = function (input, opts) { return new Promise ( function (resolve, reject) { var abort_promise = function () { reject (new Error ('fetch abort')) }; var p = oldFetchfn (input, opts).then (resolve, reject); p.abort = abort_promise; return p; }) }
Promise.race方法接受一個promise實例數組參數,表示多個promise實例中任何一個最早改變狀態,那麼race方法返回的promise實例狀態就跟着改變。
var oldFetchfn = fetch; // 攔截原始的fetch方法 window.fetch = function (input, opts) { var fetchPromise = oldFetchfn (input, opts); var timeoutPromise = new Promise ( function (resolve, reject) { setTimeout ( () => { reject (new Error ('fetch timeout')) }, opts.timeout) }); return Promise.race([fetchPromise, timeoutPromise]) }