Long Tasks: What they are and why you should avoid them
All web applications are granted a single thread, the Main Thread, which is responsible for:
- Handling user input events, like clicks and keyboard events
- Running JavaScript tasks, like React or other client business logic
- Generating frames to present pixels to the user
Each of these operations is executed in what's known as a Task.
Since there is only one Main Thread responsible for all these Tasks, any Task that takes a particularly long time to execute will clog up the thread and degrade user experience.
In this tip, we'll examine these long-running Tasks, called Long Tasks, and why you should avoid them.
Prerequisites
- You should understand the browser event loop
- You should be familiar with the Chromium F12 Profiler
What is considered a Long Task?
A Task is considered a Long Task if it takes longer than 50ms.
How Long Tasks degrade the Main Thread
As we know from my tip on the browser event loop, the Main Thread can only run one Task at a time. Any Task that is not actively running is queued in the browser's Task Queue:
While a Task is executing on the Main Thread, the Event Loop can not unload other queued Tasks from the Task Queue onto the thread.
Long Tasks are particularly problematic because they block the Event Loop from unloading the Task Queue for an extended period of time; they essentially block the Event Loop from executing any further work on the Main Thread.
Frame Degradation
The browser event loop occasionally will run the Render Steps instead of selecting a Task from the Task Queue. The Render Steps Task is responsible for presenting frames to the user's screen.
Like other Tasks, the Render Steps cannot run while another Task is running on the thread. As a result, a Long Task can severely degrade the browser's ability to generate frames to your user:
Problem: Delaying your Initial Frame
If your web application relies on JavaScript to produce your initial UI on page load, the speed to present your critical frame will depend on the speed of the JavaScript required to present it.
For example, if your app relies on React to present its initial frame, React must construct the DOM, and then the Render Steps must run, and then finally your user can see your UI:
Your user cannot see your UI until a Frame is produced, so it's important to reduce the size of your Long Task so the browser can produce the frame sooner:
Problem: Degrading Interactivity
Let's consider another example: a user is typing into an input box.
Each keypress event is queued in the browser's Task Queue and subsequently place onto the Main Thread to run by the Event Loop. The keypress is then represented on-screen via a Frame:
We want input to feel responsive and smooth for users, like this:
Notice that each keypress of the user is immediately represented as pixels on the screen via a Frame.
If a Long Task occurs while typing, your user will experience jank:
For a user, input jank will manifest as delayed frames to reflect their input events:
What causes Long Tasks?
Long Tasks typically arise from heavy, slow, or inefficient JavaScript codepaths that are synchronous within a Task.
Common examples of these codepaths include:
while
/for
loops with a high iteration count- Synchronous JavaScript work, often in high frequency, such as:
- String parsing, decoding, concatenation, etc.
- Updates to the DOM
- Compilation of large scripts
- Inefficient querying of element positioning information, inducing Layout Thrashing
- Heavy or frequent Microtasks, usually from resolved
Promise
s
Long Tasks do not only manifest to JavaScript related work, but in my experience, this is their most common source.
Identifying Long Tasks In the Profiler
If you collect a trace of your web application, the Chromium F12 Profiler will help you identify Long Tasks by flagging them in the UI:
The profiler will represent Long Tasks as a flamegraph. Follow my tip on understanding flamegraphs to learn how to read these graphs, and identify slow codepaths within a Task.
Conclusion
We've covered what Long Tasks are, and why you should avoid them.
Explore the following tips on how to optimize your Long Tasks and improve your user experience:
- Remove inefficient work from Tasks
- Defer non-critical work from your critical path
- Decompose Long Tasks into smaller tasks
That's all for this tip! Thanks for reading! Discover more similar tips matching Browser Internals and JS Optimization.