Frontend & browser·topic 3 of 13
JavaScript in the browser
One thread does the layout, the paint and your code, and it can only do one of them at a time. These are the words for what that thread is doing, in what order, and why the interface stopped responding while it did it.
Read in order · tick what you already know
- 01
your loop over ten thousand rows froze the whole tab, including the scroll and the spinner.
Main thread
- 02
the error trace reads bottom to top and shows you every function that was still waiting when it threw.
Call stack
- 03
your `setTimeout(fn, 0)` did not run immediately, it ran after the browser got a chance to paint.
Task
- 04
the promise callback ran before the timeout you scheduled first, and before the browser painted anything.
Microtask
- 05
you moved the animation out of `setInterval` and it finally stopped stuttering at odd moments.
requestAnimationFrame
- 06
you deferred the analytics work until the browser said it had nothing better to do.
requestIdleCallback
- 07
you moved the parsing off to another thread and the interface kept responding while it ran.
Web worker
- 08
you clicked the button and the click handler on its container ran too, without you binding anything there.
Event bubbling
- 09
you passed `{ capture: true }` and your handler ran before the one on the element that was actually clicked.
Event capturing
- 10
you put one click handler on the list and read `event.target` to work out which row was clicked.
Event delegation
- 11
you stopped the form from navigating away but the parent's handler still ran.
preventDefault
- 12
you stopped the click from reaching the document and three unrelated features quietly broke.
stopPropagation
- 13
the console warned that your scroll handler blocked scrolling, so you marked it passive.
Passive event listener
- 14
you wait until the user stops typing for three hundred milliseconds and only then send the request.
Debounce
- 15
you let the scroll handler run at most once every hundred milliseconds no matter how fast the events arrive.
Throttle
- 16
the callback still had the variable from the function that created it, long after that function returned.
Closure
- 17
the interval you set up on mount kept logging the count as zero no matter how many times you clicked.
Stale closure
- 18
you passed the method as a callback and it lost the object it belonged to.
this binding
- 19
you called the function on line three and it worked, even though it is declared on line forty.
Hoisting
- 20
you got 'cannot access before initialization' on a `const` that is clearly declared right there in the file.
Temporal dead zone
- 21
the object did not have the method but calling it worked anyway, because something further up did.
Prototype chain
- 22
the object had exactly the same contents as last render and the component re-rendered anyway.
Referential equality
- 23
the comparison checked each top-level property and still said unchanged, because the nested object was mutated in place.
Shallow equality
- 24
you compared the two API responses field by field, all the way down, to decide whether to update.
Deep equality
- 25
you posted the object to a worker and got a real deep copy on the other side, but the functions were gone.
Structured clone
- 26
memory in the tab keeps climbing across navigations and never comes back down.
Garbage collection
- 27
you removed the element from the page but a variable still points at it, so it never gets collected.
Detached DOM node
- 28
the component unmounted mid-request, so you cancelled the fetch instead of letting it resolve into nothing.
AbortController
- 29
the request failed and nothing happened at all — no error boundary, no message, just a line in the console.
Unhandled rejection