傳統 Ajax 已死,Fetch 永生

2022-07-05 13:06:11 字數 3867 閱讀 2062

原諒我做一次標題黨,ajax 不會死,傳統 ajax 指的是 xmlhttprequest(xhr),未來現在已被 fetch 替代。

最近把阿里乙個千萬級 pv 的資料產品全部由 jquery 的$.ajax遷移到fetch,上線乙個多月以來執行非常穩定。結果證明,對於 ie8+ 以上瀏覽器,在生產環境使用 fetch 是可行的。

由於 fetch api 是基於 promise 設計,有必要先學習一下 promise,推薦閱讀 mdn promise 教程。舊瀏覽器不支援 promise,需要使用 polyfill es6-promise 。

本文不是 fetch api 科普貼,其實是講非同步處理和 promise 的。fetch api 很簡單,看文件很快就學會了。推薦 mdn fetch 教程 和 萬能的whatwg fetch 規範

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

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

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

使用 fetch 後,頓時看起來好一點

fetch(url).then(function(response) ).then(function(data) ).catch(function(e) );

使用 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  catch(e) 

// 注:這段**如果想執行,外面需要包乙個 async function

duang~~ 的一聲,使用await後,寫非同步**就像寫同步**一樣爽await後面可以跟 promise 物件,表示等待 promiseresolve()才會繼續向下執行,如果 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

下面是重點↓↓↓

先看一下 fetch 原生支援率:

原生支援率並不高,幸運的是,引入下面這些 polyfill 後可以完美支援 ie8+ :

由於 ie8 是 es3,需要引入 es5 的 polyfill: es5-shim, es5-sham

引入 promise 的 polyfill: es6-promise

引入 fetch 探測庫:fetch-detector

引入 fetch 的 polyfill: fetch-ie8

可選:如果你還使用了 jsonp,引入 fetch-jsonp

可選:開啟 babel 的 runtime 模式,現在就使用 async/await

終於,引用了這一堆 polyfill 後,可以愉快地使用 fetch 了。但要小心,下面有坑:

竟然沒有提到 ie,這實在太不科學了,現在來詳細說下 ie

所有版本的 ie 均不支援原生 fetch,fetch-ie8 會自動使用 xhr 做 polyfill。但在跨域時有個問題需要處理。

ie8, 9 的 xhr 不支援 cors 跨域,雖然提供xdomainrequest,但這個東西就是玩具,不支援傳 cookie!如果介面需要許可權驗證,還是乖乖地使用 jsonp 吧,推薦使用 fetch-jsonp。如果有問題直接提 issue,我會第一時間解決。

由於 fetch 是典型的非同步場景,所以大部分遇到的問題不是 fetch 的,其實是 promise 的。es6 的 promise 是基於 promises/a+ 標準,為了保持簡單簡潔,只提供極簡的幾個 api。如果你用過一些牛 x 的非同步庫,如 jquery(不要笑) 、q.js 或者 rsvp.js,可能會感覺 promise 功能太少了。

deferred 可以在建立 promise 時可以減少一層巢狀,還有就是跨方法使用時很方便。

ecmascript 11 年就有過 deferred 提案,但後來沒被接受。其實用 promise 不到十行**就能實現 deferred:es6-deferred。現在有了 async/await,generator/yield 後,deferred 就沒有使用價值了。

標準 promise 沒有提供獲取當前狀態 rejected 或者 resolved 的方法。只允許外部傳入成功或失敗後的**。我認為這其實是優點,這是一種宣告式的介面,更簡單。

always 可以通過在 then 和 catch 裡重複呼叫方法實現。finally 也類似。progress 這種進度通知的功能還沒有用過,暫不知道如何替代。

fetch 替換 xhr 只是時間問題,現在看到國外很多新的庫都預設使用了 fetch。

最後再做乙個大膽**:由於 async/await 這類新非同步語法的出現,第三方的 promise 類庫會逐漸被標準 promise 替代,使用 polyfill 是現在比較明智的做法。

想不想加入阿里巴巴一起玩 es7,react,frp 等最新技術,歡迎簡歷到 [email protected]

傳統 Ajax 已死,Fetch 永生

注 這段 如果想執行,外面需要包乙個 async function 基於標準 promise 實現,支援 async await 同構方便,使用 isomorphic fetch 引入 promise 的 polyfill es6 promise 引入 fetch 探測庫 fetch detecto...

YUM已死,DNF永生

這個應該是從fedora22開始的 dnf從yum分支出來,使用 專注於效能的c語言庫hawkey進行依賴關係解析工作,大幅度提公升包管理操作效率並降低記憶體消耗,按原先的節奏本應該是fedora 22實現這一替代方案,隨著dnf 1.0版本的發布,這一刻終於到來。這樣的激進更新是不可避免的,主要是...

APP已死,服務永生

舉幾個簡單的例子來證明一下。雷軍創辦的小公尺為何會實現4年160倍的估值?有人說,雷軍會網際網路炒作。其實大錯特錯,我在 解密小公尺 網際網路思維下的商業奇蹟 一書中詳細的分析了成功的原因,順勢 定位 營銷 最高價效比,這其實根本沒有秘籍,小公尺仍然是一步步的用心做產品,做服務,並沒有以次充好。知名...