Ivan Akulov
iamakulov.com
Ivan Akulov
@iamakulov.com
Web perf engineer @ Framer. Prev. web perf consultant (Google, Appsmith, Toggl, etc). Getting React interactions 2-4x faster. GDE. He/him 🏳️‍🌈
Responded! bsky.app/profile/did:...

And learned that this doesn’t happen with every dep, only with some of them :D

(Is it a bad taste to quote-reply on bluesky btw? I haven’t posted much in a while lol, might’ve missed the memo)
Yeah, Redux works great because it doesn’t import anything!

Here’s a case when this breaks:

1) Add a random dependency in Redux that’s used in some random function:
November 26, 2025 at 11:44 PM
This doesn’t happen with every package btw.

This happens with fast-deep-equal because it’s 1) written in CommonJS (so it’s not tree-shakable) and 2) doesn’t have `sideEffects: false` (so the bundler cannot ignore the package if its imports aren’t used).

But most packages have one of those :(
November 26, 2025 at 11:42 PM
Whereas if Redux *wasn’t* bundled, the bundler won’t even enter the file that imports fast-deep-equal. (This is thanks to `sideEffects: false` which Redux does.)
November 26, 2025 at 11:42 PM
2) Make an index.js that imports and uses a completely different function:

import { compose } from "redux";
console.log(compose);

3) Bundle

4) Boom – fast-deep-equal is bundled, even though it’s not used
November 26, 2025 at 11:42 PM
Yeah, Redux works great because it doesn’t import anything!

Here’s a case when this breaks:

1) Add a random dependency in Redux that’s used in some random function:
November 26, 2025 at 11:42 PM
- esm > cjs modules (bundlers still don’t shake cjs well/at all)
- sideEffects: false ftw
- don’t bundle a library into a single file for npm. kills shakeability
November 26, 2025 at 5:36 AM
spent the day shaking the tree, shook 1.5 MB
November 26, 2025 at 5:36 AM
just why
November 16, 2025 at 5:55 PM
Using PerformanceObserver or manual timers/logs, I guess :D

Logging into the console while it’s closed still works

(But yup, somewhat annoying 🙈)
November 14, 2025 at 2:09 PM
So turns out none of this was real

(which was pretty hard to tell because the only way to tell was to close DevTools lol)
Welp, turns out it’s not real.

Just opening DevTools makes all timers 5-100× slower, due to the overhead of capturing stack traces. Even if you do nothing else (don’t record performance, etc)!

Guess who accidentally spent a weekend optimizing this 😅

(h/t @paul.irish for explaining why)
November 14, 2025 at 3:43 AM
Benchmark: codepen.io/iamakulov/pe.... Try with DevTools open vs closed – especially with call stack depth 1000.

(“1000 levels deep” might seem like a lot, but it’s pretty realistic with React’s recursivelyTraversePassiveMountEffects. That’s how I ran into it – in TanStack Query!)
November 14, 2025 at 3:42 AM
Welp, turns out it’s not real.

Just opening DevTools makes all timers 5-100× slower, due to the overhead of capturing stack traces. Even if you do nothing else (don’t record performance, etc)!

Guess who accidentally spent a weekend optimizing this 😅

(h/t @paul.irish for explaining why)
November 14, 2025 at 3:42 AM
this needs to nerdsnipe someone from Chromium to investigate this :P
November 10, 2025 at 5:12 PM
In the app I spotted this issue in (a typical complex React app), these setTimeouts were ~1500 calls down the `recursivelyTraversePassiveMountEffects` stack
November 10, 2025 at 5:12 PM
Okay, I noticed that I cannot repro these slow setTimeouts outside of React / TanStack Query.

So I tried to profile this, and apparently setTimeout calls are much (10×) slower if you do them deep within a call stack (eg React’s recursivelyTraversePassiveMountEffects)? bsky.app/profile/did:...
Okay, so this is pretty wild: apparently, in Chromium, the deeper you are in the call stack, the slower your `setTimeout()` calls become?

gist.github.com/iamakulov/85...
November 10, 2025 at 5:06 PM
setTimeout calls also become 2× slower if you have previously set ~750 timers (doesn’t matter whether they already fired):
November 10, 2025 at 4:58 PM
Okay, so this is pretty wild: apparently, in Chromium, the deeper you are in the call stack, the slower your `setTimeout()` calls become?

gist.github.com/iamakulov/85...
November 10, 2025 at 4:58 PM
hahaha, full circle :D
November 10, 2025 at 3:57 PM
I might publish the impl I posted in that issue as an npm package!
November 10, 2025 at 3:56 PM
(Question prompted by spending an hour to implement userland setTimeout batching :D)
November 9, 2025 at 10:19 PM
Why are native setTimeout and clearTimeout calls so expensive? Like, here it takes 1.5 ms. What’s the technical reason it’s so slow?

Just 100-200 of those in a row (trivial if you’re mounting a bunch of React components that set timers) will easily block the page.
November 9, 2025 at 10:19 PM
> once [2] gets shipped, you should switch to "eager".
fresh from @perfnow.nl: it will ship v soon, in Chrome 143 <3
October 31, 2025 at 2:20 PM
Belated hi from @perfnow.nl! Come talk to us (me & @kurtextrem.de) about React performance, Framer sites, LCP/INP and more :D
October 31, 2025 at 10:05 AM
Currently in Chrome Canary behind a flag (enable at chrome://flags):
October 30, 2025 at 5:29 PM