認識 JavaScript 的非同步機制與 AJAX 技術
JavaScript 在運行時是一種「單執行緒」語言,也就是一次只能處理一件事情。但實際開發中,我們常常需要進行「非同步操作」,像是請求資料、等待用戶互動、計時器等等。非同步的設計讓我們可以在等待某些事情完成的同時,繼續處理其他任務,讓使用者體驗不會被卡住。
AJAX 應用可以僅向伺服器傳送並取回必須的資料,並在客戶端用 JavaScript 處理來自伺服器的回應。因為在伺服器和瀏覽器之間交換的資料大量減少,伺服器回應更快了。同時,很多的處理工作可以在發出請求的客戶端機器上完成,因此 Web 伺服器的負荷也減少了。
AJAX 技術的出現,讓瀏覽器可以向 Server 請求資料而不需費時等待。當瀏覽器接收到 response 之後,新的內容就會即時地添入原本網頁。
非同步是什麼?
「同步」就像你在餐廳排隊等食物,但不能離開否則就等不到食物了
「非同步」則是你拿了號碼牌後,可以先去做別的事,等叫號再來取餐
非同步的本質是:發出請求之後,不會「卡住」等待結果,而是先記下這件事,讓它在未來某個時間點完成後再通知我們。
這種通知的機制,通常透過 callback(回呼函式)、Promise 或 async/await 實作。而非同步的概念,其實你早就在用,比如:
setTimeout()
、setInterval()
:等時間到再執行- DOM 事件:不知道用戶什麼時候點擊,但先寫好要發生的動作
- AJAX 請求:資料還沒回來時先做別的事,回來再處理
AJAX 是什麼?
AJAX(Asynchronous JavaScript and XML)是一種網頁技術組合,讓瀏覽器可以在不重新整理整個頁面的情況下,與伺服器交換資料。
它的核心精神是:
- ✅ 只傳送/接收必要的資料,並在前端用 JavaScript 處理回應
- ✅ 減少伺服器與瀏覽器之間的資料傳輸量
- ✅ 提升使用者互動體驗與伺服器效率
舉例來說:你在網頁中選擇縣市,下方的鄉鎮區就會根據選擇自動載入,不會重新載入整個頁面。這就是 AJAX 的功勞。
非同步處理的演進
JavaScript 實現非同步的方法不斷演進著:從 Callback 函式、Promise 到最新的 async / await。
- Callback 函式 非同步最原始的處理方式。把要執行的函式當作參數傳入:
缺點是容易產生「回呼地獄(Callback Hell)」:
- Promise
Promise 是對未來值的一種包裝,可以用 .then()
依序串接非同步操作:
好處:
- 避免層層巢狀(但還是會一層層
.then()
) - 可集中處理錯誤(
.catch()
)
- async / await(ES2017)
讓非同步程式碼看起來像同步的寫法,可讀性大大提升:
async/await
是建立在 Promise 之上的語法糖。
--- 以下待整合
發出 request 之後不需要等待 response ,可以持續處理其他事情,甚至繼續送出其他 request,等 response 傳回之後,就被融合進當下頁面或應用中。
當你在使用 DOM 事件時,其實你已經在運用非同步的概念,你不知道事件什麼時候會發生,但你可以先把要發生的函式準備好,等函式中的 callback 被呼叫的時候再執行要接著做的事。JavaScript 可以把函式當成值來傳遞,因此執行程序可以非同步的發生。
JavaScript 實現非同步的方法不斷演進著:從 callback 函式、promises 到最新的 async-await 函式。
- 所有的 AJAX 都會有對應的 API 接口,API 的接口就是一段網址,不同的網址也對應不同的「HTTP 請求方法」,請求方法必須與網址完全對應才可運作。
- AJAX 會統一由網頁端發出請求(無論新增資源或取得資源),依據不同的接口、請求方法進行請求,而伺服器會依據請求的方法、內容來進行回應。
- AJAX 會在背景送出請求取得回應,不用等待也同時可以做其他事情,等同 Queue (佇列) 的概念,收到回應才產生一個 Queue,執行並顯示在畫面。
目前常見的技術
- XMLHttpRequest (IE7 以上支援,jQuery,axios)
實務上很少直接使用原生的 XMLHttpRequest。取而代之的方法有很多種,過去較流行的是 jQuery 的 $.ajax()
,然而在 JavaScript 日趨成熟之後,許多新的替代方案應運而生:
- fetch (較新,HTML5 才有,IE11 以下不支援)。fetch 並不是 XMLHttpRequest 的升級版本,不是 jQuery 提供的
$.ajax
語法或axios
那種原生 XHR 的封裝。它是一個全新的東西,並且基於 Promise 語法結構所設計,可配合使用 Async/Await 語法,使程式更加優雅。 - axios 是一個使用 Promise base 的 Ajax 函式庫,他有許多重要功能如:
- 廣泛的瀏覽器支持
- 可支援 Node.js 從後端發送的 Http request,這意味著 axios 可以兼用於前端與後端專案。
- 直接將回應的 JSON 資料轉換成 JavaScript 的 Object,這十分方便!
常見的非同步問題 (不限 AJAX)
- 回呼地獄
- 寫法不一致
- 無法同時執行 (無法確定什麼時候開始及結束)
Promise
!https://miro.medium.com/max/1400/1*gtdCpCvoZ6Q-YVC-N4en2w.png
使用 promise 可以解決 1. 回呼地獄 2. 寫法不一致 3. 無法同時執行 的問題
- Promise 是為了解決傳統非同步語法難以建構及管理的問題。Promise 本身是一個建構函式。
- Promise 有三種狀態:
pending
,resolved
/fulfilled
,rejected
。 new Promise
內的函式會立即被執行,當resolve
得到內容後,才會執行.then
。- 要提供一個函式 promise 功能,讓他 return 並透過 new 方式建立一個 promise 物件,並必須傳入一個函式作為參數,帶有 resolve / reject 參數,分別代表成功及失敗的回傳結果。
- 已實現的狀態會透過 resolve 這個參數回傳一個結果,在調用時使用
.then
接收回傳結果;反之否決狀態會透過 reject 參數回傳並用.catch
接收,一次只有一種結果。 - 在
.then
的 resolvedCallback 中,可以得到在new Promise
中resolve
內所得到的值 (value)。 .then()
是 promise 的 (原型) 方法- 如果在
.then
的 resolvedCallback 中 return 一個值,則這個值會以 Promise 物件的形式傳到下一個.then
。 - HTML5 中的 Fetch API 也是使用它
- 基礎運用
- 串接 (避免回呼地獄 + 巢狀寫法)
- 失敗的捕捉
除了成功的方法之外也要把失敗的方法補進去 - Promise.all
所有的 promise 都回傳成功了才進入下一個任務,在此之前都是等待,但若其一回傳為失敗就進入失敗的處理狀況 - 實戰運用
async...await
只要 function 標記為 async,就表示裡頭可以撰寫 await 的同步語法,await
關鍵字只能在 async
function 中執行,而 await
就是「等待」,它會確保一個 promise 物件都解決 (resolve) 或出錯 (reject) 後才會進行下一步,當 async function 的內容全都結束後,會返回一個 promise,這表示後方可以使用 .then
語法來做連接。
在 async
函式中使用 await
關鍵字意味者:「我們請 JavaScript 等待這個非同步的作業完成,才展開後續的動作,且這個函式會回傳一個 Promise 物件」,換成 Async/Await 的話,就不必寫下 .then()
了,就像同步的程式一般,不必理會它是否為非同步。
另外,用迴圈處理非同步事件時,需要注意 ES6 後提供的許多 Array 方法都不支援 async / await 的語法,例如使用 forEach 取代 for,結果會變成同步執行