談到 Ajax,一般會想到
XMLHttpRequest,不過使用上不便,就算是標準化後的 XMLHttpRequest
            Level 1 也只是功能上的加強,開發者通常會進一步地使用程式庫封裝,像是 jQuery 的 $.get、$.post
          或 $.ajax,曾經有一陣子流行「你不需要 jQuery」,社群裏頭嚷嚷著 Fecth API
          將會取代這一切。- 粗糙的 XMLHttpRequest
從今日的角度來看,
XMLHttpRequest 確實有許多設計不足之處,首先,一個 XMLHttpRequest
          實例肩負著太多任務,包含了事件的註冊、請求標頭的設置、連線的開啟、資料的傳送、請求本體的設置、回應狀態的判斷、回應內容的取得等,完全不符合關
          切點分離(Separation of Concerns)的原則,而且設定與呼叫順序混亂,像是經常地,開發者會搞不清楚,到底是要呼叫 open
          前還是之後設定請求標頭。就算是 2011 年標準化後的 XMLHttpRequest Level 1 也沒有改變
XMLHttpRequest
          的設計,沒有適當地做職責分離也就算了,雖然增加了幾個可註冊的事件,然而依舊是採基本事件模型,而不是類似 DOM Level 2
          事件模型那樣,可以註冊多個事件。過去有不少程式庫試著封裝
XMLHttpRequest 來解決問題,例如,jQuery的 $.get、$.post
          或 $.ajax,$.ajax 可使用選項物件(Option
          object)來做更多細部設定(在 jQuery 3,$.get、$.post
          也可接受選項物件了),透過 $.ajaxSetup 等函式可設定預設值,這些設計非但隱藏了 XMLHttpRequest
          的設定細節,也將一些職責從 XMLHttpRequest 中分離出來。由於 Ajax 的處理天生就是非同步,這與開發者習慣的同步程式碼撰寫方式不同,而在非同步下順序也變得重要時,回呼地獄就會是個大問題,為此 jQuery 提供了
Deferred,而後社群中又有了 Promise/A 與 Promise/A+
          規範,jQuery 3 實現了 Promise/A+,$.ajax 可傳回 Promise
          物件,提供了 Ajax 請求時更一致的模式,可以採用像是同步的程式碼來撰寫非同步應用。- HTML5 的 Fetch API
在 2014 年 HTML5 正式標準後不久,在 Ajax 這塊出現了許多 Fetch API 的介紹,Fetch API 是 HTML5 的一部份,Google、Mozilla 在 2015 年於瀏覽器開始提供實作,一時之間許多暢談 Fetch API 取代
XMLHttpRequest
          的文章出現,也有不少直挑 jQuery 的 $.ajax 作為取代對象。從設計的角度來看,Fetch API 就像是集合了過去 Ajax 使用上一些好實踐的集合體,可獨立地建立
Headers、Request、Response
          實例,實現了職責分離,建立時可使用選項物件來進行相關設定,而 Fetch 的工廠函式 fetch
          也可接受選項物件,而傳回值是個 Promise。表面上看來,Fetch 很像在
XMLHttpRequest 上封裝了一層 Promise,
          這也是它為什麼經常被拿來與 $.ajax 對比的原因之一,因為模式乍看之下十分類似,不過嚴格來說,$.ajax
          做了比較高階的封裝,舉例來說,$.ajax 的 data
          選項指定物件時,會自動進行序列化與請求參數編碼處理,然而使用 fetch 的 body
          選項時,必須自行建立、編碼請求參數,這是因為在 Fetch 的規範前
          言中就清楚指出,fetch 的定位本來就是低階封裝。Fetch 另一個與
XMLHttpRequest 不同的地方是 Streams
          的支援,按照規範,回應物件的 body 特性會是個 ReadableStream,行
          為上與 Streams 規範中的 ReadableStream
          相同,在伺服器的回應過程中,可以透過 ReadableStream
          持續讀取瀏覽器已接收之內容,雖然過去也可以使用 XMLHttpRequest 的 responseText
          自行處理判斷、讀取想要的資料區段,然而,前者是直接處理串流資料,後者是對整個已取得之回應進行處理,本質上並不相同。在使用 Fetch 的主要考量是瀏覽器的支援度,對於不支援 Fetch 的瀏覽器,可以使用 Fetch 修補(polyfill),修補是基於
XMLHttpRequest,仿造了 Fetch API 介面,由於 XMLHttpRequest
          本身並沒有 Streams 的功能,因此在這方面功能受限。- 回頭看看 Promise
由於 Fetch 基於
Promise,在不支援 Promise 的瀏覽器上,除了
          Fetch 修補之外,還要加上 Promise 修補(更舊的瀏覽器,像是 IE8/9,還要加上 ES5
          修補等),而在介紹 Fetch 的文件中,都會談到的一些缺點,像是不支援逾時、進度處理等,其實並不是 Fetch 本身的缺失,主要是來自於
          Promise 的限制。因為 Promise/A+ 的規範,主要只有三個狀態,只能透過
resolve、reject
          從未定(pending)轉移至滿足(fulfilled)或背棄(rejected)狀態,Promise實例本身也只有 then、catch
          兩個方法來處理對應的狀態,在不施加額外設計上,自然也就無法提供逾時、進度處理等功能。若瞭解到某個 Fetch 的限制是來自於
Promise 的限制,就可以試著從設計下手來解決需求,例如,可以透過
          Promise.race 提供兩個 Promise,一個是 new
            Promise((resolve, reject) => setTimeout(() =>
            reject('timeout'), timeout),一個來自 fetch,若前者先背棄了,
          那麼 Promise.race 傳回的 Promise
          就算是背棄了,從而實現後續的逾時處理。不過,這樣的設計只個模擬,
fetch 傳回的 Promise
          依舊會執行直到進入背棄或滿足狀態,而不是真的逾時而被中斷,只是 Promise.race 傳回的 Promise
          會忽略其狀態罷了,簡而言之,在非同步的處理模式上,Fetch 是基於 Promise,因而要能在非同步處理上活用
          Fetch,就建立在對 Promise 能有多少認識。更進一步地,由於 Fetch 是基於
Promise,若開發者熟悉 Promise,
          應該也就知道可以透過 ECMAScript 6 的產生器語法,採用像是同步的流程來撰寫非同步應用,進一步地,ECMAScript 7 提供了
          async、await,無論是 Promise 本身或者是
          Fetch,都可搭配 async、await 使用(可參考先前專欄〈從產生器到 async、await〉),
          這會是它們未來的優勢之一。- Fetch 解決了什麼?
每當有新的技術或概念出現,人們總愛嚷嚷著舊的東西將會死去,新的東西會取代一切,喜新厭舊吧!舊東西誕生在舊的時代,適時地解決了當時的問題,而後 從中累積了不少的使用經驗,因而誕生了新的技術、概念或規範,急著預言舊東西將會逝去,並不會讓開發者看起來更為耀眼,只會讓開發者看不清楚新東西的 本質罷了。
舉個舊東西好了,
$.ajax 可以就這麼與 fetch 來比較嗎?高階封裝與低階
          API 可以直接比較嗎?也許某些程度上,可以用 Fetch 解決時,無需掛個 jQuery 程式庫會是件好事,然而,$.ajax
          也許可以基於 Fetch 來重構,在 Fetch 做不到的部份,使用 XMLHttpRequest 來實現,XMLHttpRequest
          畢竟也不是完全那麼不堪,或者乾脆寫個 $.fetch 之類的擴充來實現前述概念如何呢?Fetch 實際上代表的是 Ajax 從
XMLHttpRequest、選項物件、回呼處理、Promise,
          甚至往後銜接至產生器、async/await
          的這條發展路線中,各種實作與經驗的累積與修正,瞭解這個過程中的累積與修正,才會知道 Fetch
          解決了什麼,又有哪些沒解決的,別急著馬上判處某些舊東西死刑,不然,過陣子可能又會急著要處死 Fetch 了吧!
