在了解非同步之前先大概知道所謂的同步 JavaScript 是什麼,當跑一段程式碼,瀏覽器會將執行結果盡快的回傳出來 即為同步。舉例如下:
const header = document.getElementById("heading");
heading.innerHTML = "Hey!";
就像是在說:「嗨!請幫我找到 heading 元素;接著,將它的 innerHTML 屬性設定為 Hey」。這就是所謂的「同步」操作,當某個指令正在執行時,不會有其他的事情同時發生。
用日常生活來說,你跟別人打電話時,一定是聽他說完你再接他的話,不可能同時都說話這樣你哪能知道,他說了什麼?
而非同步則是像傳訊息一樣,不需要打完字還要一直等他回話,而是傳完訊息後還可以做自己的事,等看到他回完訊息後再回他。
而現在許多 Web API 功能都是使用非同步程式碼的做法,特別是那些要從外部裝置存取或抓取某些種類資源的功能,像是從網路抓取檔案、存取資料庫並回傳資料、從網路攝影機取得視訊串流等等。
在 ES2016 起新增了一個叫做 Promise 的特殊物件。 這一個 Promise 用來表示一個完成或失敗的非同步操作的物件。它代表的是一種中間的狀態。本質上,它代表瀏覽器述說著:「我承諾我會盡快給予你一個答覆」,因此名稱就叫做「 Promise 」。
下面範例使用 YouBike2.0 臺北市公共自行車即時資訊https://data.gov.tw/dataset/137993,提供的Api資料:
console.log(
fetch(
"https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json"
)
);
點開瀏覽器會看到一個等待中的 Promise 物件,Promise 物件有三個狀態:等待中、完成、與失敗,如圖:
等待中的 Promise 物件代表資料尚未取得的狀態,接著在 fetch() 的結束會鏈結三個程式區塊:
以下列程式碼為例,我們透過 fetch 函式取得資料,得到回應後,則將得到的資料轉 JSON 格式,再將轉換後的結果 console 出來會看到下圖:
fetch(
"https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json"
)
.then((resp) => resp.json())
.then((sites) => {
console.log(sites);
})
.catch((err) => {
console.log(err);
});
從上圖可以看到陣列裡包了很多個物件,物件裡都是每筆站點資訊,此時確定拿到資料後,就可以對該資料做處理。
另一個常見的處理 Promise 物件的手法是使用非同步函式,與前一個例子不太一樣的地方在於,非同步函式會自動等待 Promise 物件解析,接著才往下傳,而.then()會串接多個回呼函式作為處理器。
資料一樣使用 YouBike,可以看到在使用前要先宣告 async,在 Promise 物件解析前要使用 await 關鍵字,這會使函式暫停與此,直到 Promise 物件解析之後才開始往下進行。得到的結果與.then()相同。
const getYouBike = async () => {
try {
let resp = await fetch(
"https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json"
);
let sites = await resp.json();
console.log(sites);
} catch (err) {
console.log(err);
}
};
getYouBike();
想了解 JavaScript 非同步與-Event Loop 怎麼運作,可以看這篇文JavaScript-Event Loop