Delving into browser internals: the stack, web APIs and how the event loop works. Discover why `setTimeout(fn, 0)` gets used. Originally a +rehabstudio FE night class from 2016.
Call stack Event loop e.g. V8, Rhino, etc. Parses and runs the JS by pushing functions onto the stack. Tracks which function we’re currently executing. All the cool stuff the browser gives us to build web apps. DOM, XHR, setTimeout, device APIs, etc... Stores callbacks from Web APIs until the event loop can handle them. Pushes callbacks from the queue onto the stack. Rendering
currently executing. Functions get pushed onto the top of the stack and get popped off when executing. Think of a plate of pancakes. You put the pancakes onto the top of the pile and take one off when you want to eat them. Or an array where you can’t access by index or splice/slice, just push() and pop().
one call stack. It can only do one thing at a time.* If something blocks the stack, the program (and browser in general) will slow down or halt. *sort of a lie. Also, don’t mention Web Workers.
function liquid() { var x = add(2, 2); console.log('Snaaaake!', x); } function solid() { liquid(); console.log('Brother!'); } solid(); Pen Interactive demonstration of the stack during code execution Pen
it stands to reason that you shouldn’t flood it, or block it with slow code. If you flood the stack (e.g. with a recursive function), the browser will lock up and eventually terminate your program with a ‘call stack size exceeded’ (stack overflow) error. If you run slow code on the stack, the browser can’t do anything else like handle input or re-render until the code has finished. function ohNo() { ohNo(); } ohNo(); // stack overflow error (main program) updateSomething() shittySlowCode() X
setTimeout() call included. The call gets pushed onto the stack as usual, but then pops off and mysteriously vanishes. The rest of our program executes fine. A short time later, our setTimeout() callback reappears and is executed. How is this possible if we only have one stack in our runtime? console.log('Hello there'); setTimeout(function() { console.log('Hello once more'); }, 5000); console.log('Hello again'); < about 5 seconds later... ?
APIs outside the JavaScript runtime for programming web apps. A lot of these APIs are asynchronous e.g. they do their heavy lifting outside of the main runtime stack so as not to slow everything down. When they’re complete, they pass their callbacks onto the callback queue for processing the next time the stack empties. This is the ‘trick’ behind setTimeout(fn, 0).
job is to push the next item in the callback queue onto the runtime stack so it can be executed. If the queue is empty, it just idles and triggers rendering per frame. It will only push a callback the next time the stack is empty. If there are items holding up the stack (e.g. slow functions), the event loop won’t be able to empty the callback queue. This is why setTimeout is not a guaranteed delay - it’s the guaranteed minimum time before the callback will execute.
Web API handler immediately (0 delay). It will sit there until the stack is next empty and the event loop is able to push fn() out of the queue and onto the stack. In other words, it forces the browser to wait until any blocking code has run before running fn(). This is a perfectly valid, if hacky-feeling, way to use setTimeout. - Waiting for DOM manipulations & repaints - Allowing intensive code to complete first - ‘Yielding’ to the browser during a heavy loop