# 非同步機制 [ToC] --- ## JavaScript 為什麼不採用多執行緒 ---- ### 單執行緒的狀況下- 習慣的思維 ```javascript= const userTable = { '1': { money: 10000 } } function withdraw (id, money) { userTable[id].money -= money } function deposit (id, money) { const currentMoney = userTable[id].money currentMoney += money userTable[id].money = currentMoney } ``` ```javascript= withdraw('1', 10000) // 0 ``` ```javascript= deposit('1', 1000) // 1000 ``` ---- ### 多執行緒的狀況下- 沒有互斥鎖的狀況 ```javascript= const money1 = userTable[id].money const money2 = userTable[id].money // * money1 -= 10000 userTable[id].money = money1 money2 += 1000 // 認為餘額還是 10000 userTable[id].money = money2 // 11000, 現賺 10000 (也可能變 0) ``` --- ## 非同步仍是必要的- 畫面凍結的問題 https://codesandbox.io/s/ui-blocking-r354l --- ## 我都要! 單執行緒+非同步機制 ---- ### Event Loop (事件循環機制) ![Task and Microtask](https://i.imgur.com/tIyIN7M.png) ```javascript= function bar () { console.log('baz') } function foo () { bar() } setTimeout(() => { console.log('setTimeout') }, 1000) setInterval(() => { console.log('setInterval') }, 5000) foo() ``` ---- ### Render時機與畫面凍結 [為什麼要用 setTimeout, 只用 Promise.resolve 不可以嗎?](https://gitlab.com/cqbw_generic/web-frontend/emu/-/blob/develop/src/store/index.js#L389-391) [何時使用 microtask](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide#When_to_use_microtasks) --- ## 小試身手 - Task(Macrotask): setTimeout, requestAnimation ... - Microtask: Promise.then, process.nextTick ... ```javascript= setTimeout(() => { console.log('setTimeout') }) new Promise(resolve => { console.log('promise executor') resolve() }) .then(() => { console.log('promise then') }) console.log('main') ``` --- ## 總結 - 兩種不同的執行隊列: microtask queue、macrotask queue - 瀏覽器會在下個 macrotask 執行前, 重新渲染 UI - [升級 node 到 major 11 版本以上](https://juejin.im/post/5c3e8d90f265da614274218a) --- ## Q&A --- ## 歡迎來到真實世界 請寫出以下印出 0~9 的順序 ```javascript= async function async1() { new Promise(async resolve => { Promise.resolve() .then(() => { console.log(0) resolve() }) }) .then(() => { console.log(1) }) await async2() console.log(2) } async function async2() { async3() console.log(3) } function async3() { new Promise((resolve) => resolve()) .then(() => { console.log(4) }) Promise.resolve() .then(() => { console.log(5) }) } async1() setTimeout(function() { console.log(6) }, 0) new Promise(resolve => { console.log(7) resolve() }) .then(function() { console.log(8) }) .then(function() { console.log(9) }) ``` ## 其他挑戰 - [螞蟻金服面試題](https://juejin.im/post/6860646761392930830) --- ## 參考來源、圖片來源 - [HTML spec.: Event loop processing model](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model) - [Use reject instead of throw - you cannot use throw asynchronously](https://stackoverflow.com/a/36222960/7122623) - [Tasks, Microtasks and Schedules](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) - [掘金: 微任務、宏任務與Event-Loop](https://juejin.im/post/5b73d7a6518825610072b42b) - https://www.uniqlo.com/ <style> p { margin: 5px !important; } blockquote { border-left: 10px solid #ddd; font-size: 2.5rem; } blockquote > p { margin-left: 5px; } :not(.overview) > .slides { top: 0 !important; bottom: 0 !important; left: 0 !important; right: 0 !important; transform: none !important; width: 90vw !important; } img { display: inline; } </style>
{"metaMigratedAt":"2023-06-15T05:41:04.538Z","metaMigratedFrom":"Content","title":"非同步機制","breaks":true,"contributors":"[{\"id\":\"e77f9ae0-feb2-4c38-9ae8-f06f8655e3a4\",\"add\":14138,\"del\":10631}]"}
    358 views