9 ms·
Idle Until Urgent
- jgtrosh 8y agoReading this article and applying a similar technique to a different webpage could be a good exercise for advanced students in front end development. The core idea is put forward, and no implementation detail is left out; great article.
- thegeomaster 8y agoWhat I don't understand is why a very simple blog like this needs a ~56KB bundle of JavaScript at all.
- Cthulhu_ 8y ago^ exactly; blogs are mostly static. You can improve the responsiveness of e.g. a comments section with a bit of JS, but that's very low priority and doesn't need to be much.
- oftenwrong 8y agoThis blog works fine without javascript.
- mrspeaker 8y agoLooking at the blog post, I'd say it was for drawer.init(), contentLoader.init(), breakpoints.init(), alerts.init(), and analytics.init(). The site works fine without javascript, so it doesn't need it - perhaps the author thinks the 50k is worth the drawer, content loading, breakpoint?, and alert features and also wants a bit of user analytics tracking.
- AstralStorm 8y agoEven so, you could backload the drawer etc. placing them at the end of the document and attaching to potentially already rendered page. Reorder so that your main content loads first. Author did part of it by deferring the analytics init. Not sure why they used setTimeout though for the initialization instead of requestIdleTimeout like everything else. The page is supposed to work without JS after all. "I mentioned above that requestIdleCallback() doesn’t come with any guarantees that the callback will ever run." Not true - there's a timeout argument. It guarantees that the callback will by ran by then.
- mjdease 8y agoRegardless of the demonstrated use case, these principles can be applied to improve the performance of web pages that use JavaScript
- CydeWeys 8y agoThank you. That'd be like someone criticizing a coding example for a new language saying "What's the point of using OOP for implementing Tic-Tac-Toe? It's too simple to necessitate it."
- thegeomaster 8y agoI'm not criticizing the technique from the article. It's useful. I'm just expressing my confusion that the blog, simple as it looks, needs this JavaScript (and analytics are in a separate file from the one I'm referencing, it appears).
- philipwalton 8y agoThat 56K number isn't gzipped, gzipped it's only 18K (plus there's also some inline JS, some webpack boilerplate, and then analytics.js). The reason for its size is my site is my playground. It's where I get to experiment with all the things I want to experiment with. I also work on quite a few open source projects, which I usually test on my site before releasing them publicly just to make sure they work in production without errors.
- ummonk 8y agoYou make that sound like a lot. It's only equivalent to like 20 pages of raw plain text in size.
- neetdavid 8y agoI poked around a little and it seemed like the majority of it is related to analytics events (googleanalytics/autotrack) I suppose the author just wants to know a little about how their blog and writing is performing in the wild Sort of off topic, but it would be interesting if the browser handled these common cases instead and gave the user a way to opt-in/out. I suppose it sort of does by broadcasting those events to the js listeners in the first place.
- AstralStorm 8y agoI'm not sure why you trust Google Web "Fundamentals" that 0-100ms is perceived as instant. The upper bound is perceptibly laggy. I have no idea where Google took their numbers from. FID of 100 ms is already bad as you have to add network latencies on top of it. To put things into perspective, it's more than the time it takes to fully boot embedded Linux as coreboot from not too fast flash or start up Commodore 64 with a good extension cartridge. The browsers are terribly slow.
- treerock 8y agor.e. the 100ms, some references are given on this stackoverflow question. https://stackoverflow.com/questions/536300/what-is-the-shortest-perceivable-application-response-delay#2547903 https://stackoverflow.com/questions/536300/what-is-the-short...
- Xichom2k 8y agoI don't think scroll gestures or incrementally-loaded content with layout reflows were a thing back then, so that might need re-evaluating.
- AstralStorm 8y agoI'm pretty sure layout reflows were considered, as HTML rendering itself is incremental in almost all sensible browsers. (Thus the ideas of embedding CSS and JS in localized pieces where applicable.) Scroll gestures were not a thing typically, you just did a full request.
- Xichom2k 8y agoSure, it's incremental, but static site layouts, especially the old float-based ones will have had their headers and sidebars loaded from the start and the main content would not jump around. Modern pages with ads and widgets popping in potentially anywhere main remain unusable and unreadable because the main content keeps jumping around.
- 8y ago
- iofiiiiiiiii 8y agoNot being a JavaScript guy, my eyes sort of glazed over after the flame graphs. Do I understand the gist of it right that a 200+ millisecond delay is normal if you just have 56KB of light blog page style JavaScript code whose loading you do not somehow optimize? Or is there something pathological in play here?
- alanning 8y agoIt depends on what the JavaScript code is doing. The article discusses an example where code is loading Intl.DateTimeFormat which takes some time but is not immediately used. So if the 56KB code doesn’t do a lot of loading of expensive components then it may not need further optimization, although it may still have the problem of blocking user input. Main moral of the story is you can’t assume performance based on code size, you have to measure.
- a012 8y agoMeanwhile, there are many websites don't care if their page loads in a few seconds. For example: https://i.imgur.com/coBKPa1.jpg https://i.imgur.com/coBKPa1.jpg
- theandrewbailey 8y agoI guess I'm an old man that hasn't smoked the hype, but I don't understand why one would write this: const main = () => { setTimeout(() => drawer.init(), 0); setTimeout(() => contentLoader.init(), 0); setTimeout(() => breakpoints.init(), 0); setTimeout(() => alerts.init(), 0); requestIdleCallback(() => analytics.init()); }; over this: function main(){ setTimeout(drawer.init, 0); setTimeout(contentLoader.init, 0); setTimeout(breakpoints.init, 0); setTimeout(alerts.init, 0); requestIdleCallback(analytics.init); };
- streptomycin 8y agoThe 2nd style will not work if those functions internally use `this` and were not previously manually bound to their parent objects, like `drawer.init.bind(drawer)`.
- Xichom2k 8y agoDue to javascript's funky notion of object methods. If you just pass the function reference itself invoking it will execute it with a |this| set to undefined. Put differently, a property access x = foo.bar followed by x() is not the same as foo.bar()
- bovermyer 8y agoWhile this is impressive work, I can't help but wonder if this is solving the wrong problem. If the goal is to load fast, wouldn't it be better to just stop using JavaScript altogether? It's a blog, not an app.
- alangpierce 8y agoI don't think the author is suggesting that people should put this much effort into optimizing their blog. It's a toy example that's simple enough to explain concepts that can then be applied to bigger and more complex webapps where "don't use JavaScript" isn't an option, like with the Redux example mentioned further down in the article.
- enobrev 8y agoI don't think this comment matters, but since so many other comments on this article are speaking up against, maybe it does. I don't care, at all whatsoever, if this person wants to use JavaScript for their blog. Otherwise, nice article about improving performance and prioritizing important functionality during page-load.
- craftyguy 8y ago> I don't care, at all whatsoever, if this person wants to use JavaScript for their blog. In most cases complaining about this is 'offtopic', but in this case it is very much on-topic since the blog is about optimizing javascript usage. I'm just happy that you can actually read what they have to say without JS enabled!
- code-is-code 8y agoA few month ago, I used a similar optimisation that does the same idle-awaiting but with server-requests instead of cpu-usage. So instead of submitting all ajax to the server directly, background tasks can be delayed until the important tasks have finished. This is useful especially when the 6 requests per origin limit gets hit often. https://github.com/pubkey/custom-idle-queue https://github.com/pubkey/custom-idle-queue
- alangpierce 8y ago> Important! While browsers can run input callbacks ahead of queued tasks, they cannot run input callbacks ahead of queued microtasks. And since promises and async functions run as microtasks, converting your sync code to promise-based code will not prevent it from blocking user input! Wow, my initial reaction while reading was "just use async functions and then then code will naturally allow user input in the middle", good to know that that doesn't work.
- hinkley 8y agoThis is slowly turning into a rock in my shoe. You would think a single threaded language would implement some sort of cooperative multitasking scheme. But promises aren’t cooperative, especially in Bluebird (the queue management logic was converted to LIFO a long time ago, although that code still contained comments or variable names that imply the opposite, when last I looked). I’m tempted to call this the legacy of Brendan “design a language in a week” Eich, but that would let too many other people off the hook. IMO, webasm can’t happen fast enough.
- paulddraper 8y ago> But promises aren’t cooperative Promises are cooperative. You cannot interrupt execution (unlike, say, preemptible threads). You can only perform other execution when the current execution terminates or yields, e.g. with await. --- I think you are wanting browser UI events to preempt between Promises/microtasks. That's fair enough, but saying that Promise aren't cooperative is the wrong description.
- hinkley 8y agoThe difference between multitasking and cooperative multitasking is that you can yield the CPU in the middle of a long process. You can do that in Javascript but it involves combining multiple asynchrony APIs in complex ways. Ways you probably don’t want to invite your team to use frequently. You cannot split a large calculation in the middle by chaining promises to allow even other promises to make progress, let alone event loop processing.
- elfakyn 8y agoThe Amazon app has an absolutely horrendous FID of around 10 seconds or so. They could use a little bit of optimization.
- sequoia 8y ago[cw: tangential point] > This is the JavaScript equivalent of death by a thousand cuts. Seems to me there's one really big knife in particular, doling out most of the cuts: https://i.imgur.com/Hzrfq13.png https://i.imgur.com/Hzrfq13.png I wonder what analytics platform is contributing all this slow-down to his first interaction... ;)
- philipwalton 8y agoArticle author here. Yep, not hiding that fact (I could have easily used a trace with minified code, but I didn't to point this out). Two things though: 1. I used to work on Google Analytics, and I've created a lot of open source libraries around Google Analytics, which I use on my own site because I like to test my own libraries (and feel any pain they may be causing). The way most people use Google Analytics does not block for nearly this long. 2. I've updated my Google Analytics libraries to take advantage of this strategy [1], and I'm working with some of my old teams internally to see if they can bake it in to GA's core analytics.js library, because I strongly believe that analytics code should never degrade the user experience. [1] https://github.com/googleanalytics/autotrack/pull/235 https://github.com/googleanalytics/autotrack/pull/235
- sequoia 8y ago> Yep, not hiding that fact (I could have easily used a trace with minified code, but I didn't to point this out). Kudos to you for your honesty here! I was a bit confused by your question "So what’s taking so long to run?" when it seemed pretty clear what was taking so long to run. If the goal were simply "speed up the pageload/FID", removing browser analytics (in favor of server e.g.) would seem to be at least an _option_ to immediately achieve that end. Thanks for the article.
- philipwalton 8y agoRight, when I said "what's taking so long to run?", in my mind I was thinking there'd be one obviously slow thing that I could just remove or refactor, but it turned out that it wasn't any one single slow function/API causing the problem. And yes, clearly removing the analytics code would have also solved the problem for me, and in many cases, removing code is the best solution. In this particular case I couldn't remove any code because I was refactoring an open source library that a lot of people use. I wanted to try to make it better for input responsiveness in general, so people who use the library (and maybe don't know much about performance) will benefit for free. Also, I wanted to help educate people about how tasks run on the browser's main thread, and how certain coding styles can lead to higher than expected input latency. Anyway, glad you enjoyed the post!
- eridius 8y agoIf the measurement here is how long it takes to respond to the first user input, why does a 233ms main function matter? As a user, how am I expected to scan the page, locate a link, and click on it within that 233ms?
- jschwartzi 8y agoImagine that his site was linked somewhere else. In that scenario it takes 233ms from click to display. That's why this matters, because users aren't opening a browser to that one page. They're clicking around on different pages, and if each one takes 233ms that's a very slow process.
- benjaminjackman 8y agoHere is: - The [repo](https://github.com/GoogleChromeLabs/idlize https://github.com/GoogleChromeLabs/idlize) implementing the pattern described in the article - The [package](https://yarnpkg.com/en/package/idlize https://yarnpkg.com/en/package/idlize)