---
# System prepended metadata

title: JS | 關於非同步
tags: [JS]

---

本文想淺談 JavaScript 非同步寫法的演進。現在我們常看到的 `async` / `await` 是什麼時候出現的呢？為什麼需要這種語法呢？早期怎麼寫非同步的函式呢？接下來我們一起認識早期到現在的 JavaScript 非同步語法。


## callback function 回呼函式


一開始 JavaScript 是沒有 `Promise` 物件的，所以如果要執行非同步操作（例如： `setTimeout`、`XMLHttpRequest`），都是藉由「傳入函式作為參數」來在特定時機執行。我解釋成：

> 請幫我在特定時機，執行我給你的函式！

```javascript
function getData(callback) {
  setTimeout(() => {
    callback('資料來了（callback）');
  }, 1000);
}

getData(function(data) {
  console.log(data);
});

```

這裡的 callback 不是保留字，可以用成自訂的名稱。多個 callback 組合起來容易導致 callback hell，會變得難以維護也不好閱讀。

## Promise 物件

為了解決 callback 會有的問題，ES6 / 2015 年提出了 `Promise` 物件，並搭配 `.then()` 與 `.catch()` 的語法，來管理 Promise 的後續執行。我們可以把 `Promise` 理解成：

> 一個承諾，一旦有結果不論好壞都會告知並結束。

```javascript
function getData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('資料');
    }, 1000);
  });
}

getData()
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

```

`Promise` 物件有 `pending`、`fulfilled`、`rejected` 三種狀態。創立 `Promise` 物件時是 `pending` 狀態，可以設定「事件解決」與「事件失敗」的回呼函式，「解決」後就會變成 `fulfilled` 狀態，「失敗」的話就會變成 `rejected` 狀態。

一旦「解決」後程式就會進入 `.then()`，而「失敗」則會進入 `.catch()`。


```javascript
Promise((「[解決], [拒絕]) => {
    if (true) {
        [解決](); // Promise 完成，執行 then
    } else {
        [拒絕](); // Promise 完成，執行 catch
    }
})
```

## async/await

然而，`Promise` 的寫法仍然不夠直覺，特別是當需要多層 `.then()` 時，程式碼會變得冗長且不易閱讀。為了讓非同步程式碼更像同步的寫法，ES2017 加入了 `async` / `await` 語法。我們可以把這兩個理解成

> await：等要到結果才傳給你
> async：把 function 變成一個承諾

```javascript
async function showData() {
  try {
    const data = await getData();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}
```

`await` 是一個一元運算子，用來等待一個 `Promise` 的結果。當 `await` 後面是一個 `Promise`，它會「暫停」函式的執行直到該取得回傳值。

而如果函式內用到 `await` 時，整個函式就需要標示 `async`，這個函式便會變成一個 `Promise` 物件。其他引用這個函式的時候也要用 `await` 等它完成。

除錯時可以用 `try` / `catch` 來執行，只要 `try` 內的程式有出錯，就會執行 `catch` 內的函式。

### 參考資料：
[卡斯柏's Blog - Async function / Await 深度介紹](https://www.casper.tw/development/2020/10/16/async-await/)
[MDN - Promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise)
[MDN - await](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/await)
[MDN - async function](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/async_function)
[MDN - try...catch 語法](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/try...catch)