找了幾個 API 服務使用:

這裡使用的是 Dog API 的接口,透過 GET 來取得遠端資料。


早期取得遠端資料 GET 方法,使用 XMLHttpRequest

在早期如果要透過 AJAX 取得遠端資料,如不使用框架所使用的方式是 XMLHttpRequest 的行為方法,在使用上除了可讀性不高也不好唯護。

See the Pen 使用 XMLHttpResuest GET 執行 AJAX by Jimmy_Wu (@Jimmy_Wu) on CodePen.


什麼是 Fetch API 與 Promise

fetch:操作 AJAX 的方法

fetch() 的使用概觀

Fetch 中文翻譯為取。

MDN 文件 – 使用 Fetch 文件中指出:
Fetch API 提供了一個 JavaScript 接口,用於訪問和操縱 HTTP 管道的一些具體部分,例如請求和響應。它還提供了一個全局 fetch() 方法,該方法提供了一種簡單,合理的方式來跨網絡異步獲取資源。

fetch() 的使用有幾個特點

  • 以 ES6 的 Promise 作回應。
    • .then() 作為下一步。
    • .catch() 作為錯誤回應 (404, 500…) 的 HTTP 狀態回應。
  • return 回傳為 ReadableStream 物件,需使用不同資類型對應方法,取得資料物件。

fetch() 透過 .then() 的 Promise,接收後傳到下個 .then()

透過 fetch() API 方法執行 AJAX 行為,第一個 .then() 取得的是整個主機的回應狀態,所取得的 ReadableStream 物件使用 .json() 方法,將純文字資料轉換成 JSON 資料格式,透過 return 回傳給下一個 .then()。

第二個 .then() 是由上方第一個 .then() 所回傳出來的 JSON 資料,接收到之後就可直接將傳入的參數 jsonData 印出,就可取得一個物件資料格式。

Fetch API 的 Response 物件 ReadableStream 實體

在 Fetch API 的 Response 物件 ReadableStream 實體無法在此階段取得資資料內容,但 ReadableStream 實體下還有對應的解析方法取得裡面的資料。

  • arrayBuffer()
  • blob()
  • formData()
  • json()
  • text()

text()

在 Response 物件使用 .text() 方法查看資料,會取得的是純文字的資料。

json()

在 Response 物件使用 .json() 方法查看資料,所查看的就是當用的資料格式,可在 JavaScript 進行相關的操作。

blob()

資料轉為 blob 物件,這樣的方式是圖片的轉換方式 (這裡的圖片並非指圖片路徑,而是圖片檔案本身)。

JavaScript ES6 Fetch() 執行 AJAX

See the Pen 使用 JS ES6 Fetch 執行 AJAX by Jimmy_Wu (@Jimmy_Wu) on CodePen.


Promise:接收 AJAX 的執行結果或狀態

Promise 中文翻譯為承諾。

MDN 文件 – Promise 文件中指出:
Promise 物件代表一個即將完成、或失敗的非同步操作,以及它所產生的值。

AJAX 與 Promise 的關係

AJAX 全名為 Asynchronous JavaScript and XML (非同步的JavaScript與XML技術)。

AJAX 應用可以僅向伺服器傳送並取回必須的資料,並在客戶端採用 JavaScript 處理來自伺服器的回應。在伺服器和瀏覽器之間交換的資料大量減少伺服器回應更快了。同時,很多的處理工作可以在發出請求的客戶端機器上完成,因此 Web 伺服器的負荷減少的情形下,自然可以增加使用都端與主機交換資料的效率。

Ajax 是屬於一個透過 JavaScript 技術名稱,用於取得遠端資料。
Promise 則是一個語法,專門用來處理非同步行為,並不是專門用來處理 Ajax 使用,所以兩者是不同的。

Promise 與 Async、Await 的關係

Promise 是用來優化非同步的語法,Async、Await 可以基於 Promise 讓非同步的語法的結構,類似於同步語言,讓可讀性與易管理增加。

Promise 改善 JavaScript 非同步的語法結構

非同步與單線程的執行緒 (單執行緒) 的執行順序

JavaScript 是屬單線程的執行緒 (單執行緒) 一次僅能做一件事情的方式執行,
如遇非同步的事件時,會將非同步事件 (語法) 移到程式碼最後方執行,待所有原始碼運行完後才會執行非同步的事件。

單線程的執行緒 (單執行緒) 執行順序約為:1.開始、2.程式碼結束、3.非同步事件 (最後執行)

AJAX 行為也一樣,需確保擷取到遠端資料才繼續往下執行時,如果程式碼是依序撰寫的方式,就會無法正確呈現資料。
下面範例使用 Promise base 的 Ajax 函式庫 axios 進行一下錯誤的示範:

最後的 console.log(data);,這裡因非同步事件取資料往後執行,所以不會取得 AJAX 的資料,而是取得 data = {} 預設資料格式。
data = response; 透過賦值的方式,在確任接到 AJAX 資料時也指到最前面的 let data 全域變數中,要查看資料需要在 .then() 執行 console.log()。

Promise 結構及狀態

Promise 為建構函式需透過 new 新物件,才可使用物件下的方法

Promise 本身是一個建構函式,函式也是屬於物件的一種,可附加其它屬性方法在上。
透過 console.dir() 的結果可以看到 Promise 可以直接使用 all、race、resolve、reject 的方法,寫法如下:

  • Promise.all()
  • Promise.race()
  • Promise.resolve()
  • Promise.reject()

Promise 建構函式,可使用 new Promise() 創出物件,在原型方法 ( prototype 下) 包含 .then()、 .catch()、 finally() 方法,這些方法都要新產生物件才能呼叫使用。

Promise 建構函式建立時需傳入函式為為參數 (executor function),函式裡可包函 resolve (解決) 與 reject (拒絕) 方法選其一回傳做為 Promise 事件結束

Promise 的狀態

Promise 關鍵在處理非同步的事件,過程中包含著不同進取度狀態,如下:

  • pending:事件運行中尚未取得結果。
  • resolved:事件已執行完畢且成功操作,回傳 resolve 做為結果。(該承諾已經被實現 fulfilled)
  • rejected:事件已經執行完畢但操作失敗,回傳 rejected 的結果。


進入 fulfilled 或 rejected 就算完成後不會再改變,
Promise 中會使用 resolve 或 reject 回傳結果,
並在調用時使用 then 或 catch 取得值。

使用 console.dir() 展開函式,可見二個屬性:

  • [[PromiseStatus]]: "pending" (表示目前的進度狀態)
  • [[PromiseValue]]: undefined (表示 resolve 或 reject 回傳的值)

Promise() 執行 reject ,取得執行結果。

執行完函式直接 reject('失敗'),最終也能取得 rejected 的狀態及值。


參考資料

其他 Async function 與 Await 的使用參考:
卡斯伯 Blog – 前端,沒有極限 | Async function / Await 深度介紹