找了幾個 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 (表示 resolvereject 回傳的值)

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

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


參考資料

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