The Most Familiar Stranger - JavaScript - 非同步
Tags: javascript, beginners, chinese
Published: 2023-05-11 13:18:09

[分享記錄檔 YouTube](https://www.youtube.com/watch?v=K-aubiZUOt8&list=PLal-nRIQU5sunSCOSeZZEzUrXUXF22gep&index=10)

# 什麼是非同步

JavaScript 是一種單執行緒語言，這意味著當程式碼在執行時，只會有一個行為可以進行。由於 JavaScript 是在瀏覽器中運行的語言，它的主要用途是與網頁交互。當用戶與網頁進行交互時，瀏覽器需要執行各種行為，例如載入圖像、發送請求、處理回應等等。如果這些行為是同步的，也就是說，瀏覽器必須等待每個行為完成才能繼續執行後面的代碼，那麼瀏覽器將會凍結，用戶體驗會受到嚴重影響。

<aside>
⚠️ 單執行緒是不是就代表不能非同步呢 ?

</aside>

<aside>
🔥 **單、多執行緒**以及**同步、非同步**是不同的概念 !

</aside>

為了解決這個問題，JavaScript 提供了非同步的方式來處理這些行為。當一個行為需要等待其他行為完成才能繼續執行時，它可以將這個操作送到到一個事件佇列中，這樣瀏覽器就可以繼續執行後面的程式了。當其他行為完成後，瀏覽器將從事件佇列中取出這個行為，並且執行它。這樣可以讓瀏覽器在等待某些行為完成時繼續執行後面的程式，從而提高用戶體驗。

# Event Loop

JavaScript 的事件循環（ Event Loop ）是實現非同步行為的一種機制。JavaScript 的事件循環採用的是單執行緒模型，這意味著當程式碼在執行時，只會有一個行為可以進行。當一個行為需要等待其他行為完成才能繼續執行時，它可以將這個行為送到到一個事件佇列中，這樣瀏覽器就可以繼續執行後面的程式了。當其他行為完成後，瀏覽器將從事件佇列中取出這個行為，並且執行它。

事件循環的運作方式如下：

1. 當一個非同步行為需要執行時，它將被送到到一個事件佇列中。
2. 當所有同步行為完成後，事件循環將開始處理事件佇列中的行為。這些行為將按照它們被送到的順序依次執行。
3. 當一個事件處理完畢後，事件循環將從事件佇列中取出下一個事件，並且執行它。
4. 事件循環將不斷地從事件佇列中取出事件，直到事件佇列中沒有其他事件為止。

範例展示網站：[https://www.jsv9000.app/](https://www.jsv9000.app/)

事件循環的運作方式可以幫助開發人員更好地掌握 JavaScript 的非同步行為，從而更好地開發程式。在開發 JavaScript 時，明白事件循環是非常重要的，因為它可以幫助您更好地處理非同步行為，提高程式的可讀性和可維護性。

<aside>
💡 會排入 Microtask queue 的事件有 Promise、async / await、MutationObserver

</aside>

# Promise

在 JavaScript 中，非同步的行為通常是使用回呼函式（Callback）實現的。當一個行為完成時，會呼叫回呼函式，並且將結果作為參數傳遞給回呼函式。這種方式可能會導致回呼函式的嵌套，稱為 “回呼地獄”（Callback Hell），對程式的可讀性和可維護性造成影響。

```jsx
function getDataFromServer(url, callback) {
  setTimeout(function() {
    var data = "Some data from " + url;
		// callback
    callback(data);
  }, 1000);
}

getDataFromServer('https://example.com', function(data) {
	// another callback !
  processData(data, function(result) {
		// callback again !!
    renderResult(result, function(html) {
			// endless callback !!! 
      document.getElementById('result').innerHTML = html;
    });
  });
});
```

為了解決這個問題，ES6 引入了 Promise。Promise 是一個表示非同步行為的物件，當一個行為完成時，Promise 將返回一個結果或一個錯誤。Promise 提供了 then 方法，可以將回呼函式傳入 Promise 物件，當 Promise 物件完成時，then 方法將被呼叫。這樣可以避免回呼函式的嵌套，提高程式的可讀性和可維護性。

```jsx
// 以下是使用 Promise 的方式重寫該程式

function getDataFromServer(url) {
	return new Promise(function(resolve, reject) {
		// call api ...
		// then resolve data
		resolve(data);
	});
}

getDataFromServer('https://example.com')
	.then(function(data) {
		return processData(data);
	})
	.then(function(result) {
		return renderResult(result);
	})
	.then(function(html) {
		document.getElementById('result').innerHTML = html;
	})
	.catch(function(error) {
		console.error(error);
	});
```

# Async / Await

除了 Promise 外，ES7 引入了 Async / Await。Async / Await 是基於 Promise 的一種語法糖，它能夠更簡單地實現非同步行為。Async 函式是一個返回 Promise 物件的函式，而 Await 運算符用於等待 Promise 物件的結果。Async/Await 可以讓非同步程式碼看起來像同步程式碼，提高了程式的可讀性和可維護性。

```jsx
function getDataFromServer(url) {
  return new Promise(function(resolve, reject) {
    // call api ...
		// then resolve data
		resolve(data);
  });
}

async function processData() {
  var data = await getDataFromServer('https://example.com');
  var result = await processData(data);
  var html = await renderResult(result);
  document.getElementById('result').innerHTML = html;
}

processData();
```