Jake Archibald: In The Loop - JSConf.Asia

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

This talk has one of the best simplifying illustrations I have ever seen.

👍︎︎ 7 👤︎︎ u/dwighthouse 📅︎︎ Feb 12 2018 🗫︎ replies

Even a dumbass noob like me understands event loop. So nice.

👍︎︎ 5 👤︎︎ u/Thndrkiss13 📅︎︎ Feb 12 2018 🗫︎ replies

Microtasks, do they exhaust straight after each "yellow box"?

👍︎︎ 1 👤︎︎ u/dignat 📅︎︎ Feb 12 2018 🗫︎ replies
Captions
Hello everyone! How's it going? Let me see if I can get my slide clicker out. Yeah, this is my first time in Singapore. Yeah, it's nice. This is already going well. If I get an applause for that. That's fantastic. I haven't really looked around yet, like I landed just before the conference, so I can't say I've really experienced it yet, but I'm going to stay on for a few days afterwards, and have a proper explore. I brought my partner along with me as well and I have to say that was a big mistake, a huge mistake, because she's been really looking forward to this trip, and especially over the last couple of weeks she's been saying things like, two weeks till we go to Singapore, one week till we go to Singapore, five days to go, four days to go, and I am like, could you please not do that because what I'm hearing is five days, you only have five days to finish your talk. Four days, three days, you're still not ready, are you? She's like the worst project manager ever just taunting me on how unprepared I am, but then – she's brilliant because she knows how to put up with me because I stress. I stress pretty easily. I stress about things that aren't really problems. I stress about things that have really nothing to do with me. Someone posted this picture on Twitter last week, He's holding a MacBook between two fingers above concrete! I have not slept since I saw this picture. I stress about code like this, firstly because there's no semicolons. Give me a cheer if you use semicolons in JavaScript. Yes, good to hear. The rest of you are monsters. But that's not the main thing that stresses me out. It's because it's adding stuff to the DOM and then hiding it like presumably to show it some time later, like on click or something and that stresses me out because it's like, can we be sure that the user's not going to see a flash of that element before it's hidden and I've never been able to re-create this problem, I've never seen it happen, but you never know when it comes to race conditions. So, I’d always just swap those lines around, just so I can get some sleep. But really, there's no race condition here because the timing of running code and rendering is all tightly defined and mostly deterministic. And that is thanks to the event loop, and if I do a half decent job in the next 30 or so minutes, you'll know why things run in the order they do and I don't know, it might even make sense, but there's no promises there. So, web pages have a thing that we tend to call the main thread. Here's the main thread. We call it the main thread because loads of stuff happen here. It's where JavaScript happens. It's where rendering happens. It's where the DOM lives. And this means that the bulk of your stuff on the web has a deterministic order. We don't get multiple bits of code running at the same time, like trying to edit the same DOM and giving you a world of horrible race conditions. But it does mean that if something on the main thread takes a long time and by a long time I mean like 200 milliseconds, that's a long time in terms of user interaction, then it becomes really noticeable because it's blocked loads of other things, like it blocks rendering, it blocks interaction and I think it's difficult for us to think in this way because as humans we are extremely multi-threaded, like I can stand here, I can wave one hand, I can stand on one leg, I can wave the other leg and all the time I'm speaking, I'm breathing and processing audio and visual information. As humans we don't really have a main thread. We don't really have things that block unrelated things. I mean we have one and that is when we sneeze because as you begin to sneeze just stuff shuts down. You know? The first thing you lose is the ability to talk, then you pull a stupid face. If you're driving at this point, this is where you think “huh, I hope no one dies, least of all me.” And then the human body becomes entirely single-threaded like you were sneezing. Nothing else. You can't see, hear, think. You can move and make noises but only in ways the sneeze wants you to, you have no control over this at all. And then it's over, right? You essentially wake up and you find out if your car is still on the road, if you have the same number of limbs you started with, the same number of passengers you started with. Needless to say, we don't want to write code that is like a sneeze. So, although we have this main thread thing, we tend to spawn a whole series of threads like for networking stuff, encoding and decoding, crypto, monitoring input devices, but once these threads have done something that the page needs to hear about, they need to sort of come back to the main thread to give it that information and it's the event loop that orchestrates all of this. Take setTimeout, for instance. Is it badly named? Yes. Are the arguments in the wrong order? I'd say so. But, have you thought about how it actually works? Well, let's write a web standard for it because that's what I do these days. We'll start with... The setTimeout method, when invoked, must run the following steps: Wait ms milliseconds, invoke callback. Done. But this isn't quite working because spec text like this, this runs on the same thread as whatever invokes it, and in this case, it's invoked by JavaScript, so this is running on the main thread. So, if we say wait 5000 milliseconds, we are waiting 5000 milliseconds on the main thread. We're blocking lots of other stuff, so this spec is very sneezy right now, so we need to change that and we do this. We run the steps in parallel which is magic spec speak for get off the main thread or get off this thread and run this stuff kind of at the same time as other stuff. But we create a new problem here because now we're invoking a callback from something other than the main thread and I mean, there's no way this can really work. You would end up with lots of JavaScript running in parallel, still editing the same DOM and you'd end up with all of these race conditions. So, what we do is this, we queue a task and we queue a task to get back on to the main thread at some point and now we're calling JavaScript on the thread where JavaScript lives, so it all works. And this is a core part of how the browser works. If you click the mouse, like how does that get from the operating system into your JavaScript? It queues a task. When you fetch something how did you get the response into your JavaScript? Well, it queues a task. And you send a message from a page to a worker, once again it queues a task to do that. So, the first part of the event loop I want to look at are task queues, and this is the oldest part of the event loop. Rather than look at the spec I thought it might be easier to try and visualize the event loop. So, here it is. This is it. I hope that clears up any questions you have. Actually, I really hope it does because I based the whole talk around this one diagram, so I hope it works. But yeah, without anything to do, the event loop just spins round and round in a CPU efficient manner. Now, this visualization is running at a fraction of a percent of real time, and it's still kind of too fast to really see what's going on, so let's slow things down a bit. When we queue a task the event loop takes a detour, so this here this detour here, this is where tasks happen. So, at some point the browser says to the event loop, hey, I've got a job for you to do and the event loop is like, excellent, okay, add it to my to-do list and I'll get round to it at some point. No problem. Done. What if we do this like using setTimeout? We queue two callbacks that we want to run after 1 second, 1000 milliseconds. Well, according to the spec we wrote these two algorithms go parallel, each waits for a thousand milliseconds and then they need to come back on to the main thread, and they do that by queuing a task. So, the browser says to the event loop, hey, I've got something here that wants to do main thread work. In fact, I have two things and it adds each one as a separate to-do item in the task queue. The event loop's like, sure, that's fine, I'll get around to it. So, it runs the first callback, it goes around the event loop and runs the second callback. And that's tasks. And it would be pretty simple if that's all it was, but it gets more complicated when we think about the render steps, and this is what the browser uses to update what's actually on the screen. The render steps are another detour and that involves style calculation. This is looking at all of the CSS that's going on and working out what applies to each element. Layout, this is creating a render tree, figuring out where everything is on the page and where it's positioned. And then creating the actual pixel data, doing the actual painting. So, at some point the browser will say to the event loop, hey, you know, we need to update what's on the screen and the event loop's like no problem, I'll get around to that next time I go around the event loop. Now, I don't know about you, but I would consider myself an expert at coding badly, but I can take very simple bits of JavaScript and create infinite loops out of them in places I least expect, but let's take a closer look at what happens when I do that. Here's a page with a gif and some text and a big button that runs an infinite JavaScript loop. So, if I click that button, everything stops. The gif has stopped. I can no longer select text. The whole tab has kind of come to a standstill. Code for this is simple. This is just button click while (true). So, how do we actually visualize this? Well, the user clicks the button. So, the browser says, hey, event loop, I've got a task for you, and event loop's like yep, no problem, I'm on it. But this task never ends. It's running JavaScript forever. A couple of milliseconds later the browser says, hey, event loop, like we need to update that gif that was on the page. So, if you could just render at your next earliest convenience that would be fantastic. The event loop's like, yeah, okay, I'll get around to that right after I finish this infinite loop that I'm busy doing right now. Then the user tries to highlight text and that involves like hit testing, involves looking at the DOM to see what the text actually is. So, the browser says, hey, I've got a couple of more items for your to-do list there. And the event loop's like, are you having a laugh? Like do you know how long it takes to perform an infinite loop? It's a long time, you know. There is a clue in the name. So, that is why a while loop blocks rendering and other page interaction, but this is a good thing in practice. We look again at the code that I started with. I used to worry that this would result in a flash of content, but it can't, right, because this script runs as part of a task and that must run to completion before the browser can get back around to the render steps. The event loop guarantees your task will complete before rendering next happens. It still stresses me out though, I'll always swap these lines around when I see it in code. So, a while loop blocks rendering, but what about this? So, this is a loop, but each time we go round the loop we're using setTimeout to queue the next call. Well, let's find out. So, very similar testing like before. I click the button and things are still working. In fact, it kind of looks like nothing has changed, but in the background here's what's happening. We queue a task, go around the event loop, pick up that task and we queue another task as a result and that just keeps happening and happening until the end of time, but as we've already seen like only one task can be processed at a time. So, when it's processing a task, it's having to go all the way around the event loop to pick up the next task. So, that means at some point the browser can say huh, we should update the display for that gif and it can and it can go around and update the display, and that's why a setTimeout loop is not render blocking. But if you want to run code that has anything to do with rendering a task is really the wrong place to do it because a task is on the opposite side of the world to all of the rendering stuff as far as the event loop is concerned. What we want to do is we want to run code in the render steps. I want to run code here. And the browser lets us do that and it lets us do that using requestAnimationFrame. Another badly named function, I think, but it's really good for this purpose. RAF callbacks, they happen as part of the render steps and to show why this is useful, I'm going to animate a box, just a box using this code. So, I'm going to move that box forward one pixel and then use requestAnimationFrame to create a loop around this and that's it. That's all it does. So, that's requestAnimationFrame, but what if we switched requestAnimationFrame for setTimeout? It looks like this. Now, this box is moving faster. It's moving about 3.5 times faster, and that means this callback is being called more often, and that is not a good thing. That's not a good thing at all. We saw earlier that rendering can happen in between tasks. Yes, but just because it can happen doesn't mean it must. In reality we can take a task, should we render? No, it can't be bothered yet. Go around the event loop, pick up another task. Shall we render now? No, it doesn't feel like the right time. Many tasks can happen and before the browser goes, yeah, actually next time we will update the display and the browser gets to decide when to do this and it tries to be as efficient as possible. The render steps only happen if there's something actually worth updating. If nothing's changed, it won't bother. Like if the browser tab is in the background, if it isn't visible, it will never run the render steps because there's no point, but also the majority of screens update at a set frequency. In most cases that's 60 times a second. Some screens go faster, some screens go slower, but 60 Hertz is the most common. So, if we changed page styles like a thousand times a second, it's not going to run the render steps a thousand times a second, it will synchronize itself with the display and only render up to a frequency the display is capable of. Usually, 60 times a second. Otherwise, it would be a waste of time, like there's no point rendering stuff the user will never see, but that's what setTimeout is doing here. It's moving faster because it's updating the position of that box more times than the user can see, more times than this display is capable of showing us. Also, so far, we've been using setTimeout as this kind of shorthand for "queue a task" and it isn't really because even though we've put 0 milliseconds for the callback it's more like 4.7 is what the browser will use as a default. The spec says the browser can pick any number to use, but in the things I've tested it seems to be about 4.7. There isn't a single method that just queues a task, but we can kind of fake it using message channels. So, I ran a test with that and if you're sensitive to flashing images, it might be best to look away now because that looks like this. There're so many tasks happening that it kind of just looks like the box is getting a random position. We're getting a task every two-hundredths of a millisecond. So, rendering can happen between tasks, but you can have many, even tens of thousands of tasks between renderings. Okay, flashing image has gone now. Let's imagine each of these is a frame that is displayed to the user. So, our rendering steps they happen at the start of each frame and that includes like style calculation, layout and paint, not necessarily all three every time. Depends what actually needs updating, but I like this. I like this. This is very neat and tidy. This is a beautiful picture. Tasks on the other hand, they couldn't give a stuff. They just kind of appear anywhere they fancy. The event loop ensures that they happen in the right order, they happen in the order they were queued, but in terms of timing within a frame there is no kind of ordering here at all. And we saw this with our setTimeout. We were getting four per frame, three or four per frame and that means that three-quarters of those callbacks were wasted effort in terms of rendering. Old animation libraries used to do something like this, where they were trying to use a millisecond value that's going to give them roughly 60 callbacks per second and they're assuming a lot about the screen there. They're assuming a screen is 60 Hertz, but that was the common case. So, it kind of worked, it eliminated some of the duplicate effort. Unfortunately, it was a massive hack because setTimeout was not designed for animation and it really shows like due to inaccuracies you can end up with drift. So, what's happened here is we're doing nothing in one frame, and then in the next frame we're doing twice the amount of work, and that is a visual jank to user, it doesn't look great. Also, if one of your tasks runs long, you can end up moving the render steps around because it's all running on the same thread and you're sort of disturbing that lovely routine that they have. If we use requestAnimationFrame rather than setTimeout, it would look a lot more like this. All neat and tidy. All nice and ordered. Everything is within the timing of the frame, even this longer task here. When I see performance traces like this, this makes me happy. This is showing a good user experience. Makes me feel very calm. You can't avoid tasks completely of course because things like click events they're going to be delivered to you in a task, and generally you want to respond to those as soon as possible, fair enough, but if you have things like timers or you have stuff coming from the network, I really recommend using requestAnimationFrame to batch that work together, especially if you already have animations running because you can save yourself a lot of duplicate work. I treat tasks like I treat people who drink fizzy water, like I acknowledge that they exist but I keep our interaction to a minimum because I do not trust them at all. I mean I would consider myself an empathetic person, but I have limits, like I think soda water is totally disgusting, and I cannot think of a way that a human being could drink fizzy water without gagging or being sick or passing out or something. So, when I say someone who does drink fizzy water, I think there must be something else going on with them. I maintain a Twitter list of people who drink fizzy water. It's not a creepy thing. I just want to make sure I know what they're up to and what they're doing, but when I find the link between these people and Brexit and Donald Trump, I am blowing the case wide open, taking him straight to the FBI and it pains me to tell you this, JSConf, but this conspiracy goes straight to the top. I saw him in airport drinking fizzy water. Every now and then someone will say to me, “oh, but Jake, you drink Diet Coke, and I think you'll find the main ingredient is fizzy water, ha ha ha.” No, that's different, that's completely different like the main ingredient of air is nitrogen, right? But you would still die if that's all you breathed. So, it's more like that. You cannot survive on just fizzy water. What was I talking about? requestAnimationFrame. Right. There's one more detail I want to get to and this is something that catches a lot of developers out, it caught me out. requestAnimationFrame it comes before processing CSS and before painting. So, code like this might seem expensive, like we're showing and hiding a box many many times, but this is actually really cheap, like JavaScript will always run to completion before rendering happens. So, while you're doing this the browser just sits back, and it lets you have your fun changing a value and it doesn't really think about it in terms of CSS at all. And then at the end when it actually comes around to the render steps it goes, right, what did you actually change in the end? And the only bit that matters is this final line. And this explains a gotcha in CSS or at least something that caught me out. I had a thing, right? that I wanted to animate from an X position of 1000 to 500. And that sounds easy, right? So, I had my listener here. I set the X position to 1000, I told it to transition and I changed the value to 500, but that animated from 0 to 500 and I was like come on, browser, that isn’t what I asked you to do. It's very clear. I said 1000 transition to 500. What? I figured out that maybe I'm giving it too much information all at once. And it's the same reason we saw before like the browser is not going to think about it because I am just to install one block of JavaScript. So, it's going to ignore that first transform value. So, I go, okay, fair enough, what I'll do then is I'm going to put this, this second bit, I'm going to put inside a requestAnimationFrame and now it still animates from 0 to 500, and I was like what is going on here? Well, I will tell you what's going on because I finally figured it out. The user clicks on the button and that's a task. So, we come around to here and this is where we set the initial transform and the transition. Fine. We queue an animation frame, and we go around and this is where we set the destination, the final transform value. But the browser doesn't think about CSS until this next step over, the purple block there. This is where it calculates the CSS. So, again it totally misses the first value because it hasn't thought about styles in between those two things being set. And that's why to make this work you need to use not one but two requestAnimationFrames and now this will animate from 1000 to 500. Incidentally, there is a hacky alternative to this. You can use something like getComputedStyle and just access one of the properties on it and this forces the browser to perform style calculation a lot earlier than it naturally would, but it makes the browser take note of all of the things you set up until that point. So, it's like oh, okay, transform = translateX(1000) that's a thing that this element does. But you need to be careful with doing this because you can end up forcing the browser to do a lot of extra style work than it really wants to, it only really wants to do that once per frame. In reality the best way to deal with this would be the animation API, the web animation API, because you can just say I want it to go from here to this other value and it all just works, but that's only in Chrome. So, it's not really worth talking about right now. So, if the position of requestAnimationFrame within the render steps, if that was a surprise to you, if that was something you didn't know already, it's probably not your fault. You might have been misled by particular implementations because Edge and Safari, they get this very wrong, they put RAF around about here, most notably they put it after paint. And that's kind of annoying because it means like if the user clicks somewhere or something happens, and you want to batch that work you've been using requestAnimationFrame, Edge and Safari they will render before they get to your callback. So, the user is going to see something and that means that you're not going to see the actual changes you make until the next frame along and that's adding quite a significant delay to things appearing on the screen and it also makes it really difficult to batch work together. I hope this is something they fix soon. There's been activity on the bugs recently, but the web standards say it should be here, and that's where it is in Firefox and in Chrome. Okay, that's enough about requestAnimationFrame. I want to take a look at microtasks. This is probably the least understood part of the event loop I'd say. I strongly associate microtasks with promises, but this is not where they started. Back in the 1990s, browsers wanted to give developers a way to monitor DOM changes and the W3C went, okay, we'll sort that out for you, and they gave us mutation events. So, this is where I could say, okay, I want to know when a node is inserted into the body element. And fine, excellent, and you get a series of other events as well. But, in practice this was pretty problematic. We take this bit of code here, what I'm doing is I'm adding a hundred spans into the body element. How many events would you expect to receive as a result of this? One event? One event for the whole operation? Nope. 100 events? One for each span. Yes. But also, another hundred for this line here when the content is going into the actual span, a text node is going into the span and because these events bubble, this simple piece of code is going to land you with 200 events. And because of this like relatively simple DOM modifications ended up triggering thousands of events and if you were doing like a tiny bit of work in these listeners that quickly became a big bit of work, and it was a performance disaster. What we really wanted was a way to sort of hear about a batch of this work. It's similar to what we saw with styles before. We want the browser to kind of sit back, let us do some stuff and then at a convenient point, say, some stuff changed. Here is a kind of an event or something to represent all of those changes. We want to hear about it once not 200 times and the answer became mutation observers, and they created a new queue called microtasks. A lot of documentation I read about microtasks suggests that it happens like, I don't know, every turn of the event loop or it happens after a task or something like that and it is kind of true. There is a single place on the event loop where microtasks happen, but that is not where you'll generally encounter it. They also happen whenever JavaScript finishes executing. That means that the JavaScript stack has gone from having stuff in it to having no stuff in it and that's where we run microtasks. So, you can end up with microtasks happening halfway through a task, you can have microtasks in the render steps as part of requestAnimationFrames, kind of anywhere, anywhere JavaScript can run. So, that means this JavaScript will run to completion adding a hundred spans and their contents. JavaScript finishes executing and we get our mutation observer callback. Promises made use of them as well. So, here we queue a microtask and then log(‘Yo!’), JavaScript has finished executing. So, we go for the microtasks, and we log(‘Hey!’). And that means when the Promise callback is executing you were guaranteed that no other JavaScript is midway through at the time, the Promise callback is right at the bottom of the stack, and that's why promises use microtasks. But what happens if we create a loop using microtasks? A bit like we did with setTimeout before. Same demo again. Click the button and it blocks rendering, it blocks the tab in the same way a plain while loop did, very different from setTimeout before. So, Promise callbacks are async, fine, but what does async actually mean? I mean all it means is that they happen after synchronously executing code, so that's why we get (‘Yo!’) before (‘Hey!’). But just being async doesn't mean it must yield to rendering, doesn't mean it must yield to any particular part of the event loop. We've looked at three different queues so far. We’ve looked at task queues, animation callback queues which is where requestAnimationFrame callbacks happen and now we're looking at microtasks, and just to make your lives a little bit easier they all are processed very subtly differently. Like we've seen with task queues, we take one item and we take one item only and if another item is queued it just goes to the end of the queue. Fine. Animation callbacks they happen until completion, except ones that were queued while we were processing animation callbacks. They are deferred to the next frame. Microtasks on the other hand they are processed to completion including any additionally queued items. So, if you were adding items to the queue as quickly as you're processing them you are processing microtasks forever. The event loop cannot continue until that queue has completely emptied and that is why it blocks rendering. I get really excited about this stuff. I hope other people are excited about this as I am. Thank you. One person. Excellent. You know what, I used to have a real job like many of you did. This stuff like it's sort of speaking the standards work, the creating slides. This used to be my hobby but then my hobby became my job, and now I have no hobbies. I'm a boring person now, and I didn't actually notice when this happened, like genuinely. The first time I noticed is when I went for an eye test and the optician just making small talk asked, “and what are your hobbies?” I am like, “oh shit, when did this happen? I don't have any.” Like I said, I stress, so I got a bit like oh, I can't say nothing, who says nothing? So, it's just totally true. I panicked and I said “I play the piano.” I don't play the piano. And then I stressed even more because I thought like she was going to say, “Oh well. That's great. We don't need to use the letters chart then, here's some sheet music. Can you read me the first five bars? What chord is this?” Thankfully she didn't. But yeah, my optician now thinks I'm a pianist. That’s great. I don't go back to the opticians anymore. I'm terrified of like more piano chat. I don't know how I survive in the real world. I think you're ready for this next one. Occasionally I run little JavaScript quizzes. Maybe that's my hobby. Nope. That's work too. Never mind. Yes, I run little JavaScript quizzes, and this is my favorite question. So, I've got a button and on click I resolved a Promise and then log something. But I have two event listeners on the same element doing the same thing. So, if the user clicks a button, what happens? In which order are things logged? Well, our first listener executes. Great. So, that's on the JavaScript stack. Queues a microtask and then we get to the next line where we log Listener 1 and that's the first answer. Most folks agree on that bit, but what next? And I ran a Twitter poll on this last week. I've been speaking to a few of you, a few of you saw it. Most people would say the next thing logged is Listener 2, that's 63%, 5% of people think NaN is just logs and then Infinity, that is not the answer, but fair enough to that 5% of people, but yes, we've got 63% and Listener 2 is the wrong answer. This is a real gotcha with script, but if you thought it was Listener 2 then you're in very good company. So, don't worry about it. Our Listener has finished. So, we've gone from having something on the JavaScript stack to nothing. So, it is Microtask time. We are going to run that promise and we are going to log Microtask 1 and there we go. And then it's time for the second listener and that works the same. So, the order is Listener 1 Microtask 1, Listener 2 Microtask 2. But that's if the user clicks the button. What if the button is clicked using JavaScript? Oh, yes, it's different. For starters, our script is on the stack. We call click and that synchronously dispatches the events. So, we start with Listener 1. Great. We queue a microtask and we log Listener 1 and now it's microtask time. No. No, it is not microtask time. We can't run microtasks. This is where it's different because our JavaScript stack is not empty, button.click has not returned yet. So, we move on to Listener 2. We queue another microtask and now we log Listener 2 and this is where it diverges. And now, our listener is done. In fact, all our listeners are done. button.click returns, our JavaScript stack empties and now we can process those microtasks and they happen in order and this has real-world implications. So, beware if you're using promises in this way, if you're using automated tests as well because automated tests if they're clicking things on the page, they're likely to be using JavaScript to do it and that can change the behavior of your code. This also came up when we were looking at how to add observables to the DOM and how they'd integrate with promises. We hit this question, If we have a promise that represents the next click of a particular link, it's just this little bit of code here, can someone use that promise and still call event.preventDefault? Promises are async. So, have we missed our chance to prevent the default here? Turns out no, it's fine. It's totally fine. This works. This just works unless again the user clicks the link or some code clicks the link using JavaScript. And this is the final puzzle of the talk. I'm overrunning a little bit. To figure this out we actually need to take a look at the spec. So, this is a very rough description of how the spec for clicking a link works, but we start by creating an event object and then for every listener we have we invoke that listener with the event object and then we take a look at, has that eventObject's canceled flag been set. If it's been set then we're not going to follow the hyperlink, but if it's unset, we will follow the hyperlink. When you call event.preventDefault it sets this canceled flag on the event object. So, if the user clicks a link that's fine, our microtasks happen here after each callback because that's where the JavaScript stack empties, but when we call click with JavaScript, it's just going to call out to Process a link click algorithm, and it only returns once that algorithm is complete. So, the JavaScript stack never empties during this algorithm. So, our micro tasks can't happen. So, it hits this step 3 where it looks at the eventObject and even if you've got loads of promises trying to call like preventDefault it's too late. It's going to follow the hyperlink and then sometime later those promise callbacks happen, but you've missed the boat. You've missed the point where you can actually cancel that event. So, remember that microtasks they behave quite differently depending on the JavaScript stack. Okay. So, that 34 minutes, that was a massive brain dump of everything I know about the event loop and the various steps and various queues. I have found that knowing this stuff like it prevents that case where you kind of got a bit of code that's not doing what you want. So, you just wrap it in a setTimeout and now it kind of works sometimes. Knowing the stuff kind of helps avoid that. It helps me avoid jank by getting stuff running on the correct part of the event loop as well, and I hope I’ve managed to explain this stuff in a way that is helpful to you too. Like I said I'm a stressful person. So, as a result of this I'm now going to go and collapse in a pile, but you've been a great audience. Thank you very much. Cheers!
Info
Channel: JSConf
Views: 402,716
Rating: 4.9602351 out of 5
Keywords: JavaScript, Event Loop, Web Development, Browser, Technology
Id: cCOL7MC4Pl0
Channel Id: undefined
Length: 35min 12sec (2112 seconds)
Published: Fri Feb 09 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.