字幕列表 影片播放 列印所有字幕 列印翻譯字幕 列印英文字幕 >> (Phillip Roberts) hello, come in and sit down. (Phillip Roberts))大家好,請進、請坐 So for the last session before the afternoon break, we have Phillip Roberts who works at 所以在下午休息時間之前的最後一節時間 我們邀請到Phillip Roberts Andea and is here from London ‑‑ Scotland. 他任職於&yet公司,並從倫敦...是蘇格蘭 Edinbrough. 愛丁堡過來的 ‑‑ wow, ten second memory, he's going to talk about the vent loop. 哇,才一下就忘了 他接下來會來談談Event Loop If everyone could give Phillip a big brownedder round of applause. 請大家給Phillip掌聲歡迎他 >> Phillip Roberts: Okay hello everyone, thanks for coming to the side track, it's awesome Phillip Roberts: 大家好,謝謝大家前來這個小場次 能看到這裡有滿滿的人 to see it packed out in here. 感覺非常棒 Can everyone give me a stretch. 現在能請大家做個伸展動作嗎? I needed to stretch, so I look less weird. 因為我需要伸展一下,這樣我看起來才不會奇怪 I want to talk about the event loop and what the heck is the event loop, as in the event 我想要談一下Event Loop(事件循環) JavaScript裡面的Event Loop loop inside JavaScript. Event Loop到底是什麼東西 So first up, as he said I work for AndYet which is an awesome little Dev shop in the 首先,我說過我任職於&yet公司 我們是間小型而出色的美國公司 US, look us up if you need help with real‑time stuff. 如果有人需要即時性事情的幫忙 歡迎查一下我們的資訊 That's what we're good at. 那是我們的專業 So, about 18 months ago--I'm a paid professional JavaScript developer--I thought to myself 所以呢,大約18個月之前,我是個JavaScript工程師 我在心中想著 how does, like JavaScript actually work? JavaScript到底是怎麼樣運作的呢? And I wasn't entirely sure. 我當時並不是完全肯定 I'd heard V8 as a term, chrome's Runtime didn't really know what that meant, what that did. 當時我有聽過V8引擎、chrome的Runtime這些詞 但不清楚是什麼意思,或是有什麼功能 I'd heard things like single threaded, you know obviously I'm using callbacks. 我聽過一些東西,像是Single Thread(單執行緒) 我很明顯是在用Callback(回調) How do callbacks work? Callback是怎麼運作的? I started a journey of like reading and research and experimenting in the browser which basically 於是我開啟了一段閱讀、研究 和在瀏覽器上的實驗的過程 started like this. 一開始是長這樣子的 ‑‑ I was kind of like JavaScript what are you. 我那時心裡有點在想:“JavaScript,你是什麼鬼啊?” I'm a single threaded single concurrent language ‑‑ right. 然後它回我說:“我是個單執行緒的程式語言” ...好喔 yeah, cool, I have a call stack, an event loop, a callback queue, and some other APIs 好,我有一個Call stack(呼叫堆疊)、一個Event Loop(事件循環) 一個Callback queue(回調佇列),還有一些API and stuff. 等等的東西 ‑‑ rite. 好 I did not do a computer science degree. 我沒有念過電腦科學學位 I mean, these words, they're words, so I heard about V8 and the various Runtimes and different 我是說,這些名詞的意義我不懂,所以我聽過V8引擎 還有幾個Runtime函式,還有不同的瀏覽器 browsers so I looked to V8 do you have a call stack, an event loop, a callback queue, and 所以我看了V8引擎,問說:你有沒有呼叫堆疊 事件循環、回調佇列... some other APIs and stuff, I have a call stack and a heap, I don't know what those other 還有其他API等等的...我有呼叫堆疊,還有堆積 我不知道其他的東西是什麼 things are, okay, interesting so basically 18 months passed. 好,很有意思...於是18個月基本上就過去了 And I think I get this. 然後我覺得我搞懂了 (Laughing) and so, this is what I want to share with you today. (笑聲)所以,這就是我今天想和大家分享的內容 Hopefully this will be useful if you're relatively new to JavaScript, help you understand why 如果你是JavaScript的新手的話 希望這些對你有幫助,幫助你了解 JavaScript is so weird when you compare it to other languages you might used why callbacks 為何JavaScript和其他你可能用過的程式語言 相比這麼古怪,為什麼回調 are a thing, cause us hell but are required. 是重要的東西,造成我們一堆痛苦 但是又躲不掉 And if you're an experienced JavaScript developer hopefully give you some fresh insights how 如果你是一位經驗豐富的JavaScript開發人員 那我希望可以給你一些關於 the Runtime you're using works so you can think about it a little better. Runtime是如何運行的新見解 讓你能夠多深入思考一下 So if we look at the JavaScript Runtime itself like V8 which is the Runtime inside Chrome. 所以,我們可以看一下JavaScript Runtime V8引擎就是Chrome中的Runtime This is a simplified view of what JavaScript Runtime is. 這個是JavaScript Runtime的簡化示意圖 The heap, where memory allocation happens, and then there's the call stack, which is Heap記憶體,就是進行記憶體分配的地方 然後是呼叫堆疊,這個是 where your stack frames are and all that kind of stuff, but, if you, like, clone the V8 堆疊框架,還有所有相關的東西所在的位置 不過,如果比如說你複製V8引擎的程式碼庫 code base and grep for things like setTimeout or DOM or HTTP request, they're not in there, 然後用grep抓取一些像是setTimeout 或是DOM或是HTTP請求的東西,那些並不在那裡 they don't exist in V8, which was a surprise to me. 那些並不在V8引擎裡 這件事讓我大吃一驚,對吧? It's first thing you use when you start thinking about async stuff and it's not in the V8 source. setTimeout有點像是用JavaScript、想到非同步時 會用的第一樣東西,而那竟然不在V8引擎裡 Hmm ... interesting. 嗯...很有意思 So, over this 18 months of discovery I come to realize this is really, this is really 因此,在經過這18個月的發現之中 我逐漸了解到這確實是 the bigger picture, this is what I'm hoping to get you on board with today and understand 更大的概念,這就是我今天希望 帶大家認識的東西,了解一下 what these pieces are, we have the V8 Runtime but then we have these things called web APIs 這些東西是什麼,我們有V8引擎的Runtime 但是我們也有叫做Web API的東西 which are extra things that the browser provides. 而Web API是瀏覽器所提供的額外的東西 DOM, AJAX, time out, things like that, we have this mythical event loop and the callback 像是DOM、AJAX、setTimeout之類的東西 然後我們還有神秘的Event Loop,以及... queue. callback queue(回調佇列) I'm sure you've heard some of these terms before, but maybe you don't quite understand 我很確定大家都聽過一部分的專有名詞了 但也許並不是太理解 how these pieces pull together. 這些東西是如何拼湊在一起的 So, I'm going to start from the beginning, some of this will be new, to words might be 所以,我要從頭開始講起 有些字詞對於一部分人可能很陌生 new to people, other people will get this. 一部分人可能聽得懂 We're going to quickly move on from here, bear with me if this is obvious, I think for 不過我們要開始快速講下去 如果太簡單的話,請忍耐一下,但我認為 a lot of people it's not. 對大多數人來說並不簡單 So, JavaScript is a single threaded programming language, single threaded Runtime, it has 所以呢,JavaScript是一種單執行緒的程式語言 單執行緒的Runtime a single call stack. 它有單執行緒的呼叫堆疊 And it can do one thing at a time, that's what a single thread means, the program can 它可以一次做一件事 那就是單執行緒的意思,程式一次 run one piece of code at a time. 可以跑一段程式碼 So, let's try and visualize that just to get our heads around what that mean, so if I have 所以,我們來試著將它視覺化 來幫助我們為了解它的意思,所以如果我有 some code like this on your left, we've got a few functions, a function multiplier which 一些左手邊的程式碼 這裡有一些函式,有乘法函式 multiplies two numbers, square which calls multiply with the same number twice, a function 能將兩個數字相乘 平方函式,把同一個數字乘以兩次 which prints the square of a number of calling square and then calling console.log and then 有個函式是如果我要平方功能,然後呼叫console.org 它就將一個數字的平方結果印出來 at the bottom of our file we actually run print square, this code all good? 然後在我們檔案的底下,我們執行了printSquare 這段程式大家都懂嗎? Make sense? 合理嗎? Cool. 好 So, if we run this, well, I should back up a step, so the call stack is basically ‑‑ 所以,如果我們執行它,等等,我應該倒回一步 抱歉,所以Callstack基本上 it's a data structure which records basically where in the program we are, if we step into 是一種資料結構,它能夠記錄幾種情況下 我們在程式中的哪個位置,比如說進入 a function, we put something on to the stack, if we return from a function, we pop off the 一段函式、比如我們在stack(堆疊)上放某個東西 從函式回傳結果的時候、我們取下stack頂端的東西 top of the stack that's all the stack can do, ‑‑ so if you run this file, there's 那些就是stack能做的事情... 所以如果你執行這個檔案 kind of a main function, right, like the file itself, so, we push that on to the stack. 裡頭有個主函式,對吧,就像這個檔案本身 所以我們把這個放到堆疊上 Then we have some function definitions, they're just like defining the state of the world, 接著還有一些函式定義 就像是定義了整個世界的狀態 and finally we got to print square, right, so print square is a function call, so we 最後還有printSquare函式 printSquare是一種函式呼叫方式 push that on to the stack, and immediately inside print square, push on to the stack, 所以我們把它推到堆疊上 然後在printSquare裡面,推到堆疊上的 which calls multiply, now we have a return statement, we multiply A and B and we return, 再呼叫Multiplt,現在我們有一個return statement(回傳陳述) 我們會把A和B相乘,然後回傳 when we return we pop something off the stack, so, pop, multiplier of the stack, returning 回傳的時候,我們會把某個東西從stack上移除 所以,我們移除相乘指令,回到square to square, return to print square, console.log, there's no return, it's implicit, because 回到printSquare、console.org 現在是printSquare、沒有回傳指令,這是隱含的 we got to the end of the function, and we're done so that's like a visualization of the 因為我們已經走到函式的盡頭,這樣就走完了 所以這就是呼叫堆疊的示意圖 call stalk, does that make sense? 這部分可以理解嗎? (Yes, Phil) even if you haven't thought about the call stack before, you've come across (可以,Phil)就算你以前沒有想到過呼叫堆疊 你也一定有碰過 it when you've been doing browser‑side development, so if we have code like this, a function baz 那就是當你在瀏覽器端做開發的時候 所以假設我們有個這樣的程式,一個名為baz的函式 which calls bar, which calls Foo, which throws an error if we run it in Chrome we see this. 呼叫bar函式,而bar函式又呼叫Foo函式,最後丟出錯誤訊息 如果在Chrome上執行,我們會在Dev Tools看到這個...未被捕捉的錯誤:Oops And it prints the stack trace, right, the state of the stack when that error happened, 程式會印出堆疊追蹤記錄,對吧? 在錯誤發生時,堆疊的狀態 so, uncaught error oops Foo, bar, Baz, anonymous function, which is our main. 所以我們看到:未被捕捉的錯誤: Oops Foo、bar、Baz、匿名函式,也就是我們的主程式 Equally, if you've heard the term like blowing the stack, this is an example of that. 同樣地,如果你有聽過一些像是堆疊爆炸 的名詞的話,這就是其中一個例子 Have a function foo which calls Foo , so what's going to happen ? We have a function main foo函式呼叫foo函式的話,會怎麼樣? 這個嘛...我們跑主程式 which calls foo which calls foo, which calls foo, which calls foo, and ultimately chrome 呼叫foo函式,它再去呼叫foo函式 然後再呼叫foo函式,然後再呼叫foo函式,到最後chrome會說 says, you probably didn't mean to call foo 16,000 times recursively, I'll just kill things 你可能不是真的要重複呼叫foo函式一萬六千次 所以我會幫你中止一些 for you and you can figure out where your bug lies, right. 然後你就能釐清bug在哪裡了,對吧 So although I may be representing a new side of the call stack you have some sense of it 雖然我可能是在呈現呼叫堆疊的新的一面 你們可能多少已經在 in your development practice already. 開發的實作過程中多少了解一些了 So, the big question then comes is like what happens when things are slow? 所以,接著浮現的大哉問是 程式執行速度緩慢時會怎麼樣? So, we talk about blocking and blocking behavior and blocking, there's no strict definition 我們會講到“阻塞”,還有阻塞的行為 阻塞其實沒有一個嚴格的定義 of what is and didn't blocking, really it's just code that's slow. 來說明什麼是阻塞、又什麼不是 它其實只是一段很慢的程式 So console.log isn't slow, doing a while loop from one to ten billion is slow, network requests 所以說console.org並不慢 執行一段一到一百億的while迴圈有點慢 are slow. 網路請求很慢 Image requests are slow. 圖片請求很慢 Things which are slow and on that stack are what are blocking means. 跑起來很慢、同時又在該堆疊上的東西 就是阻塞的意思 So heres a little example, so let's say we have, this is like a fake bit of code, getSynchronous, 這裡有個小例子,假設我們 弄一串假的程式碼:getSynchronous right, like jQuery is like, AJAX request. 就好比jQuery...AJAX請求 What would happen if those were synchronous requests, forget what we know about async 如果這些是同步請求的話,會怎麼樣? 先忘掉非同步回調這件事 callbacks they're synchronous. 都先當作同步就好 If we go through it like we have, we call getSync and then we wait, because then we're 如果瀏覽一下我們走過了哪些東西 我們呼叫了getSync,然後我們等了一下,因為後來 doing network request, network is relative to computers, are slow, hopefully that network 我們在做網路請求 網路相對於電腦是很慢的,希望那段網路請求 requests completes, we can move on, wait, move on. 已經完成了,然後我們就能繼續跑... 等待、繼續跑... Wait, and, I mean, this network request might never finish, so ... yeah, I guess I'll go 等待,然後,這段網路請求可能永遠不會跑完 所以...我想我不如 home. 回家好了 Finally those three, you know blocking behaviors complete and we can clear the stack, right. 最後那三個,blocking(阻塞)行為都已經完成 然後我們就能清除stack了,對吧? So in a programming language is single threaded you're not using threads like say Ruby, that's 所以在一個單執行緒的程式語言中 不能像Ruby般使用多個執行緒 what happens, right, we make a network request, we have to just wait till it's done, because 就是那樣子,對吧,我們做了一個網路請求 我們只能等到它跑完為止 we have no way of handling that. 因為我們沒有處理這種情況的方法 Why is this actually a problem? 而這點為什麼是個問題呢? The problem is because we're running code in browsers. 它之所以為問題 是因為我們是在瀏覽器裡執行程式的 So, let's you ‑‑ here we go, okay. 我們來...這裡,好 So this is just, this is Chrome, this is the code I just ran. 這個是Chrome,這裡是我剛剛跑過的程式 Browsers don't give us ‑‑ well they do give us synchronous AJAX request, I'm faking 瀏覽器不會給...噢,它其實會給我們 同步AJAX請求,我現在用一個大while迴圈 this out with a big while loop, because it's synchronous, I basically while loop for five 來假裝做到這件事,因為while 迴圈是同步的 我基本上會在繼續跑下去之前 seconds before continuing, so if I open up the console here. 先做五秒的while迴圈,所以如果我在這裡打開console We can see what happens, so with request foo.com, why this is happening, I can't do anything, 我們就能看到發生了什麼事,所以我們對foo.com 提出請求,所以我們會想:怎麼會這樣?我什麼都做不了 right, even the run button hasn't finished rerendering the fact that I clicked it. 就算是“執行”按鈕 即使我剛剛按過也沒未rerender(重新渲染)完成 The browser is blocked, it's stuck, it can't do anything until those requests complete. 因為瀏覽器這時候被阻塞了,卡住了 直到請求都完成之前,它什麼都做不了 And then all hell breaks loose because I did some stuff,it figured that out I'd done it, 然後整個死結都打開了 因為我做了一些事,它發現我已經完成了 it couldn't actually render it. 它只是不能呈現結果 Couldn't do anything. 什麼都不能做 That's because if that call stack has things on it, and here it's got these yeah, it's 這是因為如果那段Call Stack上有東西的話 而這邊有...對... still going. 它還在跑 We've got the synchronous request, the browser can't do anything else. 我們現在有同步請求 那瀏覽器其他事就做不了了 It can't render, it can't run any other code, it's stuck. 它無法顯示畫面、無法跑其他程式碼,都卡住了 Not ideal, right if we want people to have nice fluid UIs, we can't block the stack. 情況並不理想,對吧?如果我們希望人們 能看到流暢的 UI,我們就不能阻塞Stack So, how do we handle this? 那我們要怎麼處理呢? Well the simplest solution we're provided with is asynchronous callbacks, there's almost 這個嘛...我們所能用到的 最簡單的解法就是Async Callback(非同步回調) no blocking functions in the browser, equally in node, they're all made asynchronous, which 瀏覽器中幾乎沒有Blocking(阻塞式)函式 在Node裡也是一樣,都是Async(非同步)的 basically means we run some code, give it a callback, and run that later, if you've 這基本上表示我們執行一些程式碼 給它回調,然後再執行,如果你已經 seen JavaScript you've seen asynchronous callbacks, what does this actually look like. 看過JavaScript,你就有看過非同步回調 它實際上是什麼樣的呢? Simple example to remind people where we're at. 這裡有個簡單的例子 來提醒大家我們的問題在哪 Code like this, console.log hi. 像這樣的程式碼,console.log('Hi') Write, we run the setTimeout, but that queue's the console log for future so we skip on to 我們執行setTimeout函式,但是那段佇列 不知為何是之後會出現的console.log,所以我們直接 JSConf and then five seconds later we log "there" right, make sense? 跳到JSConfEU,然後五秒鐘後 我們就紀錄了“there”,對嗎? Happy. 行嗎? Basically that's setTimeout is doing something. 基本上,那個setTimeout函式正在做事 So, asynchronous callbacks with regards to the stacks we saw before ... how does this 所以,我們之前看過的堆疊的 這一方面的非同步回調來說...是怎麼 work? 運作的呢? Let's run the code. 那麼,我們就來跑程式吧 Console.log hi. setTimeout. console.log('Hi')、setTimeout函式 好,接下來會怎麼樣? We know it doesn't run immediately, we know it's going to run in five seconds time, we 我們都知道它沒有立刻執行 我們知道它要五秒鐘後才會跑 can't push it on to the stack, somehow it just disappears, we don't have like a way 我們無法把它推到堆疊上 不知為何地它就消失了,我們還沒有方法 of describing this yet, but we'll come to it. 去描述這件事,但是等一下會再講回來 We log JSConfEU, clear, five seconds later somehow magically "there" appears on the stack. 我們打印JSConfEU、清除 五秒鐘後,不知為何,“there”就神奇地出現在Stack上了。 How does that happen? 這是怎麼回事? And that's ‑‑ this is basically where the event loop comes in on concurrency. 這基本上就是是事件循環 在並發執行上會發揮作用的地方 Right, so I've been kind of partially lying do you and telling you that JavaScript can 好,所以我一直算是沒講出完整的事實 告訴你們JavaScript只能 only do one thing at one time. 一次做一件事情 That's true the JavaScript Runtime can only do one thing at one time. 沒有錯,JavaScript Runtime 一次只能做一件事 It can't make an AJAX request while you're doing other code. 你在進行其他程式的時候 它就無法做出AJAX的請求 It can't do a setTimeout while you're doing another code. 你在進行其他程式的時候 它就無法做出setTimeout的請求 The reason we can do things concurrently is that the browser is more than just the Runtime. 而我們能夠同時做事的原因是 瀏覽器不只有Runtime而已 So, remember this diagram, the JavaScript Runtime can do one thing at a time, but the 所以,把這張圖記起來 JavaScript Runtime一次只能做一件事 browser gives us these other things, gives us these we shall APIs, these are effectively 但是瀏覽器會提供我們其他東西 這些都是你無法取得的執行緒 threads, you can just make calls to, and those pieces of the browser are aware of this concurrency 你只能呼叫它們 而瀏覽器知道一點,同步並發這時候 kicks in. 就發揮作用了 If you're back end person this diagram looks basically identical for node, instead of web 如果你是個後端工程師 那麼這張圖表和Node基本相同 APIs we have C++ APIs and the threading is being hidden from you by C++. 我們使用的其實是C++ API,而不是 WebAPI 而執行緒是讓C++ API從你眼前隱藏起來了 Now we have this picture let's see how this code runs in a more full picture of what a 現在我們有了這張圖片,讓我們看一下這段程式 如何在瀏覽器中運行 browser looks like. 有個更完整的概念 So, same as before, run code, console log hi, logs hi to the console, simple. 所以,和以前一樣,執行程式碼、console.log('Hi') console出現log('Hi'),很簡單 now we can see what happens when we call setTimeout. 現在我們可以看到呼叫setTimeout時會發生什麼 We are ‑‑ we pass this callback function and a delay to the setTimeout call. 我們把這個回調函式和一個延遲時間 丟給setTimeout Now setTimeout is an API provided to us by the browser, it doesn't live in the V8 source, 現在setTimeout是瀏覽器提供給我們的API 它不在V8引擎中 it's extra stuff we get in that we're running the JavaScript run 那像是我們從環境中額外得到的東西 我們現在就來 time in. 執行JavaScript Runtime The browser kicks off a timer for you. 瀏覽器為你啟動了一個計時器 And now it's going to handle the count down for you, right, so that means our setTimeout 現在它會為你倒數計時 所以這表示著我們的setTimeout呼叫 call, itself is now complete, so we can pop off the stack. 現在已經完成,因此我們可以從堆疊中移除 “JSConfEU”, clear, so, now we've got this timer in the web API, which five seconds later JSConfEU...清掉了 所以,現在在Web API中有一個計時器,它五秒鐘後 is going to complete. 就會完成工作 Now the web API can't just start modifying your code, it can't chuck stuff onto the stack 現在,Web API不能只是直接修改你的程式碼 它不能在準備就緒時 when it's ready if it did it would appear randomly in the middle of your code so this 就將東西丟到堆疊上,因為如果這樣 它就會隨機出現在你的程式碼之中 is where the task queue or callback queue kicks in. 所以這裡就是任務佇列或回調佇列 發揮作用的地方了 Any of the web APIs pushes the callback on to the task queue when it's done. 基本上,在跑完之後 任何Web API都會將回調推送到任務佇列 Finally we get to the event loop, title of the talk, what the heck is the event loop 最後,我們要談到事件循環,也就是這場演講的主題 事件循環到底是什麼 is like the simplest little piece in this whole equation, and it has one very simple 它就像是這整個方程式當中 最單純的小拼圖,它只有一個非常簡單 job. 的工作 The event loops job is to look at the stack and look at the task queue. 事件循環的工作是查看堆疊 並查看任務佇列 If the stack is empty it takes the first thing on the queue and pushes it on to the stack 如果堆疊是空的,它就會將第一個東西 放到佇列上,並將其堆到堆疊上 which effectively run it. 讓堆疊能有效執行 So here we can see that now the stack is clear, there's a callback on the task queue, the 所以,在這裡我們能看到堆疊已經清空了 任務佇列上有一個回調 event loop runs, it says, oh, I get to do something, pushes the callback on to the stack. 事件循環執行,它說:“噢,我得做些事情 來,把回調推到堆疊上” Remember it's the stack is like JavaScript land, back inside V8, the callback appears 要記得這個堆疊是JavaScript的領域,對吧 這回到了V8內部,所以呢,回調出現在 on the stack, run, console.log “there”, and we're done. 堆疊上、執行、console.log('there') 然後就完成了 Does that make sense? 可以聽懂嗎? Everyone where me? 大家都有跟上嗎? Awesome! 太棒了 Okay. 好的 So, now we can see how this works with probably one of the first encounters you would have 所以,現在我們能看到 在你可能第一次使用非同步工具時 had with Async stuff which for some weird reason someone says says you have to call 這是如何一起運作的,非同步工具 出於某些奇怪的理由,有人說,噢你必須 setTimeout zero, ‑‑ okay, you want me to run the function in zero time? 把setTimeout設定為零 好,你要我在這個函式用時間零來執行嗎? Why would I wrap it in a setTimeout? 我為什麼要把它包在setTimeout裡呢? Like the first time you run across this, if you're like me,i see it doing something, but 就像第一次遇到這種情況一樣 如果你跟我一樣,我看得出來它在做些什麼 I don't know why. 但是我不知道為什麼 The reason is, generally, if you're trying to defer something until the stack is clear. 原因是,通常你會嘗試推遲某些任務 直到到堆疊被清空之前,對吧? So we know looking at this, if you've written JavaScript, that we're going to see the same 因此,現在看到這個,我們就會知道 如果你已經寫好了一段JavaScript,那就會看到相同的結果 result, we're going to see “hi” “JSConf”, and “there” is going to appear at the 我們會看到Hi、我們會看到JSConfEU 還有there會神奇地出現在 end. 最後 We can see how that happens. 我們可以看到那是怎麼來的 The setTimeout zero, now it's going to complete immediately and push it on to the queue, remember setTimeout時間設零,現在它很快就要完成 然後會把它推送到佇列 what I said about the event loop, it has to wait till the stack is clear before it can 要記住,我所介紹的關於事件循環的特性 它必須等到堆疊清空後才可以 push the callback on to the stack, so your stack is going to continue to run, console.log 把回調推到堆疊上 所以你的堆疊就能繼續執行console.log “hi”, “JSConfEU” and clear, now the event loop can kick in and call your callback. “Hi”、“JSConfEU”,然後清空 現在事件循環就能夠啟動、呼叫你的回調了 That's like an example of setTimeout zero, is deferring that execution of code, for whatever 就像setTimeout時間設零的例子一樣 無論原因,都把程式碼的執行 reason to the end of the stack. 推遲到堆疊的最後 Or until stack is clear. 或是直到堆疊清空為止 Okay. 好 So, all these web APIs work the same way, if we have AJAX request, we make an AJAX request 所以,所有的這些Web API都以相同的方式運作 如果有AJAX請求,我們就發出AJAX請求 to the URL with a callback, works the same way, oops sorry, console log, “hi”, make 給帶有回調的URL 方法相同,啊抱歉,console.log(' Hi') an AJAX request, the code for running that AJAX request does not live in JavaScript Runtime 提出AJAX請求,我們用在執行該AJAX請求的程式碼 不在JavaScript Runtime裡面 but in the browser as a web API, so we spin it up with a callback in the URL, your code 而是以Web API的樣子出現在瀏覽器裡 所以我們用URL裡的回調來啟動 can continue to run. 你的程式碼能繼續執行了 Until that XHR request completes, or it may never complete, it's okay, the stack can continue 在XHR請求完成之前,或者可能永遠不會完成之前 都沒有關係,堆疊可以繼續執行 to run, assuming it completes, gets pushed to the queue,picked up by the event loop and 假設執行完了,它就會被推入佇列 被事件循環抓起來 it's run. 然後它就會執行了 That's all that happens when an Async call happens. 這就是非同步呼叫出現時 會出現的全部情況了 Let's do a crazy complicated example, I hope this going to work, if you haven't realized 好,我們來做一個超級複雜的範例 我希望他能夠成功,如果你還沒有發現到 all this is in keynote there's like I don't know 500 animation steps in this whole deck. 這些全都在演講主題之中,這投影片裡...我不知道 全部可能有500個動畫步驟 (code blows up, flames animation) (Applause) J Whew ... no ... so ... interesting, we're (代碼炸掉、動畫燒掉)(鼓掌) 噢…嗯…很有意思 given a link. 我們得到了個連結 Hmm ... is this big enough, can people see? 嗯...這樣夠大嗎?大家看得到嗎? Okay, so basically I wrote this talk for Scotland JS, after the talk I broke half of the slides 好,基本上我是今年早一點的時候,幫ScotlandJS大會 寫了這段演講,也講了一次,後來我弄壞了一半的投影片 and could not be bothered to redo all the slides because it was a total pain in the 然後完全不想費心思重新做投影片 因為 ass in keynote to do it so I took much easier route (Laughing) of writing a tool that can 實在太麻煩了,所以我採用了比較簡單的方式...(笑聲) 主要是在寫出一個能夠 visualize the JavaScript Runtime at Runtime, and it's called loop. 在Runtime將JavaScript Runtime視覺化的工具 這個東西叫做迴圈 So, let's just run this example and, which was kind of the example that we had on the 所以,我們就來執行這個範例 這有點像是我們在前一張投影片 previous slide, I haven't shimmed XHR yet, it's doable I just haven't done it. 做的東西,我還沒有shim XHR 是可以這麼做,我只是還沒有做 As you can see the code, we're going to log something, this is a shim around addEventListener, 如你所看到程式碼的樣子,我們要記錄一些內容 這個shim程序是針對addEventListener setTimeout and we're going to do a console.log. ‑‑ setTimeout,然後我們要做一個console.log I'm going to run it and see what happens so ... add a DOM API, add a timeout, code is 我接下來要執行它,看看會發生什麼事 所以呢,加一個DOM API,添加一個timeout going to continue to run, pushes the callback into the queue which runs, and we're done. 程式會繼續跑,將回調推到正在運行的佇列中 然後我們就完成了 If I click on here then it's going to ... trigger the web API, queue the callback for the click 如果我點擊這裡,它就會...觸發Web API 為點擊動作將回調排入佇列 and run it. 然後執行 if I cluck a hundred times we can see what happens. 如果我點擊了一百次 我們就可以看到會發生什麼事,對吧? I clicked, the click doesn't get processed immediately, itself gets pushed to the queue, 我點擊了,但我這次點擊不會立即得到處理 它本身會被推入佇列 as the queue gets processed, eventually my click is going to get dealt with, right. 隨著佇列得到處理 最終我的點擊也會得到處理,對吧? So I have a few more examples I'm going to run through here. 所以,我在這裡還有一些範例 要繼續帶大家看 Here we go, okay, so, I'm just going to run through a few examples just to kind of talk 除了...我不知道怎麼看tab...好,要開始囉,好的,所以呢 我要通過幾個例子來說明 about a few things that you might have run in to and not thought about with Async APIs, 一些可能在接觸非同步的API時 會遇到的一些事情,以及沒考慮過的事情 In this example we call setTimeout four times with the one second delay, and console.log 在這個範例中,我們以一秒鐘的延遲 呼叫了setTimeout四次,然後執行 “hi”. console.log('Hi') 所以,如果我們執行下去 By the time the callbacks get queued... that fourth callback we asked for a one second 我們會看到執行了幾次限時,等到回調程序排進佇列時 我們要求要一秒鐘延遲的第四個回調程序 delay, and it's still waiting, the callback hasn't run, right . 它還在等待,該次回調還沒執行,對吧? this illustrates the ‑‑ like what time out is actually doing, it's not a guaranteed 這有點說明了...就像限時實際上的功能 這並不是個 time to execution, it's a minimum time to execution, just like setTimeout zero doesn't 執行上的保證,而是距離執行的最短時間 就像是setTimeout設定為零並不會 run the code immediately it runs the code next‑ish, sometime, right? 立刻執行程式 而是接下來的某個時間才執行,對吧? So ... in this example I want to talk about callbacks, so, depending on who, speak to 所以呢,在這個範例中,我想談談回調 那麼,依據說話的對象 and how they phrase things, callbacks can be one of two things, callbacks can be any 用字遣詞的方式 回調這個動作有兩種情況:回調可以是 function that another function calls or callbacks can be more explicitly an asynchronous callback 任何一個函式所呼叫的任何一種函式 或者更明白地說,回調可以是非同步回調 as in one that will get pushed back on the callback queue in the future. 在那種情況下,動作會在後來 被推回到回調佇列 This bit of code illustrates the difference, right. 這段程式闡述了當中的區別 The forEach method on an array, it doesn't run, it takes a function, which you could 在陣列上的forEach方法不會執行 它需要一個函式,你可以在之上 call a callback, but it's not running it asynchronously, it's running it within the current stack. 呼叫一個回調,但是它不會非同步執行 而是在當前的堆疊中執行 We could define an asynchronous forEach so it can take an array, a callback and for each 我們可以定義一個非同步的forEach 好讓它可以接受一個陣列、一個回調,並且在每個 item in the array it's going to do a setTimeout zero with that callback, I guess this should 陣列中的項目上,它將使用這個回調 去執行設定為零的setTimeout,對吧,我想這應該 pass in the value, but any way, so, I'm going to run it and we can see what the difference 要傳出值,不過...沒關係,所以呢 我接下來要執行它,然後看看有什麼不同 is, so for the first block of code that runs, it's going to sit and block the stack, right? 所以對於執行的第一段程式 它將停下來,然後阻塞堆疊 Until it's complete, whereas in the Async version, okay, it's slowed down, but we're 直到程式跑完為止,然而在非同步版本中 它的速度變慢了,但是我們 basically going to queue a bunch of callbacks and they're going to clear and then we can 基本上要把一堆回調排進佇列 然後它們會清空,之後我們就可以 actually run through and do a console.log. 實際整串去執行,並且用上console.log In this example the console.log is fast, so the benefit of doing it asynchronously is 在這個範例中,console.log的執行速度很快 非同步執行的好處 not obviously but let's say you're doing some slow processing on each element in the array. 不太明顯,但假設你正在對陣列中的 每個元素進行某種緩慢的處理 I think I have that shown somewhere no, no, I don't. 我覺得我有放在某個地方...沒有...我沒有 Okay. 好 So let's say ‑‑ Ooops. 所以假設說...哎呀 So I have a delay function which is just slow, it's just a slow thing. 所以我有一個延遲函式,它很慢 它就是個很慢的東西 So ... let's say processing Async and here processing Sync. 所以...假設說處理非同步 然後這裡處理同步 Okay, now, I'm going to turn on a thing I've literally hacked together this morning, which 好,現在我要打開一個 今天早上真的才湊在一起的東西 is to simulate the repaint or the render in the browser, something I haven't touched on 這是用來在模擬瀏覽器中的重繪或顯示 我還沒碰過的部分是 is how all of this interacts with rendering ‑‑ I've kind of touched on it but not really 所有的這些與顯示交互作用的方式 我算是有種碰過了,但還沒 explained it. 講解到 So, basically the browser is kind of constrained by what you're doing javaScript, the browser 因此,基本上瀏覽器算是 被你正在執行的JavaScript所約束 would like to repaint the screen every 16.6 milliseconds, 60 frame a second is ideal, 瀏覽器想要每16.6毫秒重新繪製一次螢幕 每秒60幀是理想的選擇 that's the fastest it will do repaints if it can. 那會是瀏覽器理想上會進行的 最快速的重新繪製的頻率 But it's constrained by what you're doing in JavaScript for various reasons, so it can't 但是由於許多原因 它受到你在JavaScript中所做的操作的限制 actually do a render if there is code on the stack, right. 所以當堆疊上有程式碼的時候 瀏覽器其實並不能顯示,對吧? Like the render kind of call is almost like a callback in itself. 顯示這樣的呼叫本身幾乎就像是回調 It has to wait till the stack is clear. 它必須等到堆疊清空為止 The difference is that the render is given a higher priority than your callback, every 差別在於,顯示的優先級別高於回調 16 milliseconds it's going to queue a rend, wait till the stack is clear before it can 它每16毫秒就會把顯示排進佇列 等到堆疊清空後,它才能 actually do that render. 實際顯示畫面 So this is ‑‑ this render queue is just simulating a render, every second it's can 所以這就是...這個顯示佇列只是在模擬顯示畫面 它每秒鐘都在問... I do a render? 我可以顯示畫面嗎? Yes, can I do a render? 可以,我可以顯示畫面嗎? Yes. 可以 Where, because our code isn't doing anything now. 這裡的話,因為我們的程式碼現在什麼也沒做 If I run the code, you can see while we're doing this slow synchronous loop through the 如果我運行程式,你就能看到 我們正在對這個陣列進行同步迴圈 array, our render is blocked, right, if our render is blocked you can't select text on 我們的顯示被擋住了,對吧 如果我們的顯示被擋住了,你就無法在螢幕上 the screen, you can't click things and see the response, right, like the example I showed 選擇文字,你無法點擊物件或是看到回應 就像我稍早的時候秀出來的 earlier. 範例一樣 In this example, okay, it's blocked while we queue up the async time out, that relatively 在這個例子中,好,它在我們排隊執行 非同步限時的時候被擋住了,那相對起來算快 quick but we're given ‑‑ we're kind of giving the render a chance between each element 但是我們...我們算是在每個元素之間 給了顯示畫面一個機會 because we've queued it up asynchronously to jump in there and do the render, does that 因為我們已經將它用非同步方式 排進佇列,好讓它能夠跳進來顯示 make sense? 這邊可以懂嗎? >> Yeah >> Yeah, cool. 好 So, that's just kind of ‑‑ this is just like a simulation of how the rendering works, 所以,這只是...這像是一種模擬 模擬顯示畫面的運作方式 but it just really shows you when people say don't block the event loop, this is exactly 但這裡確實向你展示了 當人們說不要阻塞事件循環時,他們在說的 what they're talking about. 就是這個 They're saying don't put shitty slow code on the stack because when you do that the 他們說的是,不要在堆疊上放慢到不行的程式 因為這樣做的話 browser can't do what it needs to do, create a nice fluid UI. 瀏覽器就無法執行它需要做的工作 也就是建立一個品質好而流暢的UI This is why when you're doing things like image processing or Animating too many things 這就是為什麼當你執行像是圖像處理等等 或像是做太多動畫時 gets sluggish if you're not careful about how you queue up that code. 要是你沒有注意程式碼是如何排進佇列的話 畫面就會變得遲鈍 So an example of that, we can see with the scroll handlers ‑‑ so scroll handle ‑‑ 所以這裡舉一個例子,我們可以用捲軸來看 所以,捲軸... like scroll events in the DOM trigger a lot, right, they trigger like ‑‑ I presume 就像DOM中的捲軸事件會觸發很多東西一樣 它們會觸發...我猜 they trigger on every frame like every 16 milliseconds, if I have code like this this 如果我有這樣的程式碼 它們就可能會每16毫秒就在每一幀上觸發動作 right. 對吧? On document.scroll, animate something, or do some work. 在document.scroll上,製作動畫或進行一些工作 If I have this code, like as I scroll it's going to queue up like a ton of callbacks 如果我有這段程式碼,比如在我捲軸的時候 它就會像一大堆的回調一樣排進佇列 right. 對吧? And then it has to go through and process all of those and each of the processing of 然後,它必須經歷並處理每個 those is slow, then, okay, you're not blocking the stack, you're flooding the queue with 緩慢的程式碼...然後,好 你不再阻塞堆疊了,而是用排進佇列的事件 queued events. 來淹沒整個佇列 So, this is like just helping visualize, I guess, what happens when you actually trigger 所以,我想這是在幫助我們做視覺化 針對的是當實際觸發所有的回調時 all these callbacks, there's way you can debounce that to basically say okay, we're going to 會發生什麼事,有一種方法是你可以 對其做防抖動,直到基本上你能說,好,我們要 queue up all those events, but let's do the slow work every few seconds or until the user 把所有這些事件排進佇列,但我們要 每隔幾秒鐘慢慢做,或是直到使用者 stops scrolling for some amount of time I think that's basically it. 不再使用捲軸一段時間 嗯...酷,我想內容基本上就是這樣 There's a whole other talk in how the hell this works. 關於這到底是怎麼運作的 現場還有另一場分享 Because basically in running the code, like this code runs at Runtime, right, and it's 因為,基本上在Runtime執行像這樣的程式碼的時候 slowed down by I run it through a Esprima a JavaScript parser, I insert a big while 我用Esprima這個JavaScript的parser (語法分析器)的時候 程式會慢下來,在每行程式碼之前和之後,我都插入一個while迴圈 loop, that takes half a second, it just slow motions the code. 執行它要花半秒鐘 所以它基本上會讓程式變得像在做慢動作 Ship it to web worker and do a whole bunch of stuff to visualize what's happening while 把它運送給Web Worker,並進行大量工作 來對正在發生的事情做視覺化 doing it at run time that makes sense. 這是在Runtime執行的時候,就是這樣子 A whole other talk in that. 大會還會有一場完整的分享 I'm super excited about it and will talk to anyone about it after because I think it's 我超級期待想要分享的 而且會在之後和任何有興趣的人交流,因為我覺得 kind of neat, so with that, thanks very much ( applause) 這蠻讚的,那麼...以上 非常感謝大家聆聽(掌聲)
B1 中級 中文 美國腔 執行 呼叫 同步 程式碼 瀏覽器 請求 事件循環到底是什麼?(What the heck is the event loop anyway? | Philip Roberts | JSConf EU) 19 0 羅仕瑋 發佈於 2021 年 08 月 29 日 更多分享 分享 收藏 回報 影片單字