>> (Phillip Roberts) hello, come in and sit
down. So for the last session before the afternoon
break, we have Phillip Roberts who works at Andea and is here from London ‑‑ Scotland. Edinbrough. ‑‑ wow, ten second memory, he's going
to talk about the vent loop. If everyone could give Phillip a big brownedder
round of applause. >> Phillip Roberts: Okay hello everyone, thanks
for coming to the side track, it's awesome 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 loop inside JavaScript. So first up, as he said I work for AndYet
which is an awesome little Dev shop in the 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 how does, like JavaScript actually work? 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. I'd heard things like single threaded, you
know obviously I'm using callbacks. How do callbacks work? 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. 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 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 browsers so I looked to V8 do you have a call
stack, an event loop, a callback queue, and some other APIs and stuff, I have a call stack
and a heap, I don't know what those other things are, okay, interesting so basically
18 months passed. 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 is so weird when you compare it
to other languages you might used why callbacks are a thing, cause us hell but are required. And if you're an experienced JavaScript developer
hopefully give you some fresh insights how the Runtime you're using works so you can
think about it a little better. So if we look at the JavaScript Runtime itself
like V8 which is the Runtime inside Chrome. This is a simplified view of what JavaScript
Runtime is. The heap, where memory allocation happens,
and then there's the call stack, which is where your stack frames are and all that kind
of stuff, but, if you, like, clone the V8 code base and grep for things like setTimeout
or DOM or HTTP request, they're not in there, they don't exist in V8, which was a surprise
to me. It's first thing you use when you start thinking
about async stuff and it's not in the V8 source. Hmm ... interesting. So, over this 18 months of discovery I come
to realize this is really, this is really 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 which are extra things that the browser provides. DOM, AJAX, time out, things like that, we
have this mythical event loop and the 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 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 at the bottom of our file we actually run
print square, this code all good? Make sense? Cool. So, if we run this, well, I should back up
a step, so the call stack is basically ‑‑ 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 top of the stack that's all the stack can
do, ‑‑ so if you run this file, there's 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 push that on to the stack, and immediately
inside print square, push on to the stack, which calls multiply, now we have a return
statement, we multiply A and B and we return, when we return we pop something off the stack,
so, pop, multiplier of the stack, returning to square, return to print square, console.log,
there's no return, it's implicit, because 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 it when you've been doing browser‑side development,
so if we have code like this, a function baz which calls bar, which calls Foo, which throws
an error if we run it in Chrome we see this. 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. 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 which calls foo which calls foo, which calls
foo, which calls foo, and ultimately chrome says, you probably didn't mean to call foo
16,000 times recursively, I'll just kill things for you and you can figure out where your
bug lies, right. 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 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, right, like jQuery is like, AJAX request. 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 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. So in a programming language is single threaded
you're not using threads like say Ruby, that's 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. Browsers don't give us ‑‑ well they do
give us synchronous AJAX request, I'm faking this out with a big while loop, because it's
synchronous, I basically while loop for five seconds before continuing, so if I open up
the console here. We can see what happens, so with request foo.com,
why this is happening, I can't do anything, right, even the run button hasn't finished
rerendering the fact that I clicked it. 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 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. So, how do we handle this? Well the simplest solution we're provided
with is asynchronous callbacks, there's almost no blocking functions in the browser, equally
in node, they're all made asynchronous, which 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. Simple example to remind people where we're
at. Code like this, console.log hi. Write, we run the setTimeout, but that queue's
the console log for future so we skip on to JSConf and then five seconds later we log
"there" right, make sense? Happy. Basically that's setTimeout is doing something. So, asynchronous callbacks with regards to
the stacks we saw before ... how does this work? Let's run the code. 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. 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 only do one thing at one time. That's true the JavaScript Runtime can only
do one thing at one time. It can't make an AJAX request while you're
doing other code. It can't do a setTimeout while you're doing
another code. The reason we can do things concurrently is
that the browser is more than just the Runtime. So, remember this diagram, the JavaScript
Runtime can do one thing at a time, but the 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 APIs we have C++ APIs and the threading is
being hidden from you by C++. 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. now we can see what happens when we call setTimeout. We are ‑‑ we pass this callback function
and a delay to the setTimeout call. Now setTimeout is an API provided to us by
the browser, it doesn't live in the V8 source, it's extra stuff we
get in that we're running the JavaScript run time in. 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 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 is going to complete. Now the web API can't just start modifying
your code, it can't chuck stuff onto the stack 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. 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 on the stack, run, console.log “there”,
and we're done. 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? Why would I wrap it in a 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 result, we're going to see “hi” “JSConf”,
and “there” is going to appear at the 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 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 “hi”, “JSConfEU” and clear, now the
event loop can kick in and call your callback. That's like an example of setTimeout zero,
is deferring that execution of code, for whatever 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 to the URL with a callback, works the same
way, oops sorry, console log, “hi”, make an AJAX request, the code for running that
AJAX request does not live in JavaScript Runtime but in the browser as a web API, so we spin
it up with a callback in the URL, your code can continue to run. Until that XHR request completes, or it may
never complete, it's okay, the stack can continue 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. (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 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. 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. As you can see the code, we're going to log
something, this is a shim around addEventListener, setTimeout and we're going to do a console.log. ‑‑ I'm going to run it and see what happens so
... add a DOM API, add a timeout, code is 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 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 about a few things that you might have run
in to and not thought about with Async APIs, In this example we call setTimeout four times
with the one second delay, and 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 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 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 item in the array it's going to do a setTimeout
zero with that callback, I guess this should 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. In this example the console.log is fast, so
the benefit of doing it asynchronously is 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 would like to repaint the screen every 16.6
milliseconds, 60 frame a second is ideal, 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 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 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. 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 they trigger on every frame like every 16
milliseconds, if I have code like this this right. On document.scroll, animate something, or
do some work. 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 slowed down by I run it through a Esprima
a JavaScript parser, I insert a big 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 doing it at run time that makes sense. 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)
I totally thought it was going to be this video: Jake Archibald: In The Loop - JSConf.Asia 2018. I really liked his visualizations.
If you're a JS developer, especially a Node dev, and you want to dive down into some C stuff, try experimenting with the libuv project (which underpins Node and acts as its event loop). Not only will you learn C but also have many of the concepts explained in this (very good) talk shown to you in all their gory details.
I watch this every time before any js interview
I love this video...one of the best technical talks I've seen. I remember the first time I saw it....I thought to myself, "hmm.. a half-hour video about the JS event loop...I know all about the event loop...let's give it 30 seconds before I move on to the next video..." and then, 27 minutes later, "where did the last half-hour go?"
Very informative, entertaining, and engaging.
It's a shame this gets downvoted only because it's about JavaScript. It's a deep dive into the inner workings of the runtime and very well explained. Just the type of content that r/programming needs. I guess our transformation to r/programmingcirclejerk is well on its way.
They're pretty much the same as WINAPI event loops which have been here since Win3.1 if not before. WM_TIMER, WM_PAINT, WM_IDLE and the like. You could even use your own custom messages after WM_USER. All you needed to do to customize your window was create your own WndProc(), and then pass any unhandled messages back to DefWndProc() if I remember correctly.
I saw this about 2 years ago and it is the first time all this stuff made sense to me. My conceptual model of the Javascript VM is based almost entirely on this video.
Old as hell, but an ultimate evergreen.
If you want to know more about the event loop, micro and macro-task queues (subtle differences between how setTimeout and promises get queued) then read anything and everything by Jake Archibald, he has a blog post and a few videos that deep dive into how it all works. Probably the best resources I've come across.