1. 程式人生 > >使用Fetch實現非同步通訊

使用Fetch實現非同步通訊

XMLHttpRequest 是一個設計粗糙的 API,不符合關注分離(Separation of Concerns)的原則,配置和呼叫方式非常混亂,而且基於事件的非同步模型寫起來也沒有現代的 Promise,generator/yield,async/await 友好。

Fetch 的出現就是為了解決 XHR 的問題,拿例子說明:

使用 XHR 傳送一個 json 請求一般是這樣:

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 後,頓時看起來好一點

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 來做最終優化:

注:async/await 是非常新的 API,屬於 ES7,目前尚在 Stage 1(提議) 階段,這是它的完整規範。使用 Babel 開啟 runtime 模式後可以把 async/await 無痛編譯成 ES5 程式碼。也可以直接使用 regenerator 來編譯到 ES5。
try { let response = await fetch(url); let data = response.json(); console.log(data); } catch(e) { console.log(“Oops, error”, e); } // 注:這段程式碼如果想執行,外面需要包一個 async function
duang~~ 的一聲,使用 await 後,寫非同步程式碼就像寫同步程式碼一樣爽。await 後面可以跟 Promise 物件,表示等待 Promise resolve() 才會繼續向下執行,如果 Promise 被 reject() 或丟擲異常則會被外面的 try…catch 捕獲。

Promise,generator/yield,await/async 都是現在和未來 JS 解決非同步的標準做法,可以完美搭配使用。這也是使用標準 Promise 一大好處。最近也把專案中使用第三方 Promise 庫的程式碼全部轉成標準 Promise,為以後全面使用 async/await 做準備。

另外,Fetch 也很適合做現在流行的同構應用,有人基於 Fetch 的語法,在 Node 端基於 http 庫實現了 node-fetch,又有人封裝了用於同構應用的 isomorphic-fetch。

注:同構(isomorphic/universal)就是使前後端運行同一套程式碼的意思,後端一般是指 NodeJS 環境。
總結一下,Fetch 優點主要有:

語法簡潔,更加語義化
基於標準 Promise 實現,支援 async/await
同構方便,使用 isomorphic-fetch