API for
Main Thread Scheduling
Example app: Search-as-you-type
Lot of deadlines to juggle:
Main Thread Contention
Script is the most frequent source of user responsiveness issues.
There is a LOT of script in modern web apps.
It is difficult for web developers to build applications that are (and remain) responsive.
Long script makes it easy to miss deadlines
Guidelines for smooth interactions:
Typing is input and should occur within 10ms
Results are interaction response and should take < 100ms
Rendering work < 16ms (~8ms)]
Need Priorities.
A strategy for Guarantees of Responsiveness
Do work in chunks to avoid blocking.
Give chunks different priorities.
We need something that knows about our tasks, and executes them at the best time -- a Scheduler.
Prior Art
Userspace Schedulers
Scheduling has proven to result in:
better guarantees of responsiveness.
Maps Scheduler
Maps manages multiple types of interactions and events which can happen concurrently
Loading results shouldn’t impact the performance of panning the map
Loading map tiles while panning shouldn’t jank the pan gesture
They do this by scheduling all work and giving higher priority to input response tasks (user-interactive tasks)
Maps Scheduler
React Scheduler (source)
Uses rAF to run the scheduler and yield (with postmessage). Heading towards not yielding (and not using rAF) when sufficient signals are available..
React Scheduler: “idletick”
Ember Framework
created "microtask priority queue" with 3 buckets:
Ember Framework
Not yet yielding to the browser.
Concerned about other un-prioritized work eg. xhr, browser initiated callbacks, GC etc.
What do these Schedulers have in common?
What are the building blocks?
What it takes to build a Scheduler?
What does the run-loop need?
run-loop requires knowledge of:
What does the run-loop need?
Ideally run-loop can effectively coordinate with other work on the main thread:
Coordination with Rendering pipeline
Next Frame
rAFs
Document Lifecycle
(Style, Layout, Paint)
idle scripting
(best effort)
Available today:
input handler, rAF, rIC
settimeout, postmessage etc.
what happens here is undefined
Fear of Yielding
Since random things can run between frames, JS schedulers are motivated to not yield.
Maybe this is okay if they have enough signals to make a good decision?
At the least need: isInputPending, isRenderPending
Funny Thing: Platform’s event-loop is essentially a run-loop and can schedule JS :)
Maybe just use the platform directly without JS scheduler?
What’s needed
input
handlers
Next Frame
rAFs
Document Lifecycle
(Style, Layout, Paint)
idle scripting
User-blocking
work
(must fit current frame)
Default
tasks
(prepare for next frame)
“user blocking” priority
“default” priority
Alternatives for “default” priority work
Option A:
Built-in platform scheduler
Potential API (link to sketch)
Standardize on input handlers timing.
Expose a unified API.
Expose semantic priority for tasks:
Default task queues
function mytask() {
...
}
myQueue = TaskQueue.default("user-blocking");
taskId = myQueue.postTask(myTask, <list of args>);
User defined task queues
myQueue = new TaskQueue(‘myCustomQueue’, "default");
myQueue.postTask(task, <list of args for task>);
User-defined task queues enables the app to manage a group of tasks:
Option B:
“standardized” JS scheduler
JS Scheduler
Important Issues
to be tackled
Top Issues cited by schedulers
Thoughts? Ideas?
Email: panicker@chromium.org
DM: twitter.com/shubhie
https://github.com/spanicker/main-thread-scheduling