Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Cases #16

Open
benjamingr opened this issue May 10, 2020 · 3 comments
Open

Use Cases #16

benjamingr opened this issue May 10, 2020 · 3 comments

Comments

@benjamingr
Copy link

Hey,

I'm trying to figure out what use cases this API is good for. It looks pretty cool and I recently saw it's implemented in Chrome (also cool).

The README mentions a complicated use case of main thread contention like a "search as you type" but I'm not sure I grok why this API would address this.

I know there is a TODO there to add an entire document of use cases - but I think it would go a long way (motivationally) to just upload an example of:

  • The example from the README without the use of this API demonstrating the problem this API solves.
  • The example from the README with the use of this API - demonstrating how it solves them.
@shaseley
Copy link
Collaborator

shaseley commented May 15, 2020

Thanks for the suggestions, I agree that we should add those things.

One of the main use cases we're targeting with postTask is existing userspace schedulers. Many of them have a shape similar to the following:

  • Accumulate tasks in a (userspace) priority queue
  • Queue a scheduler macrotask with the browser via postMessage, setTimeout, etc. that will:
    • Run some number of tasks out of the priority queue
    • Decide to yield and reschedule the macrotask
    • Repeat

A couple important bits here are the yielding and scheduling of the macrotask. Apps and frameworks want to yield to keep the page responsive (input and rendering) and also not starve out other high priority work, e.g. I/O.

postTask aims to replace the "queue a macrotask" step. The priority in this case helps the browser make decisions about which tasks it should run before running the next iteration of the scheduler task. For example, if the scheduler is processing work associated with a user input — but still chunking work to keep the UI responsive and MT animations smooth — they could use 'user-blocking' priority to indicate to the browser that the user is waiting for this work to finish. The browser could then use that to schedule other work accordingly (e.g. after yielding to rendering, resume the postTask task instead of servicing timers).

Of course other non-userspace script can use this API (setTimeout is often used to yield back to the event loop) — often in conjunction with an underlying scheduler (e.g. in the framework). In this case, the priorities help coordinate.

It's also worth noting that we're considering exposing the priorities to async APIs like fetch and IDB so they can be scheduled as part of the same system.

I'll work on adding use cases and example code to better illustrate these benefits in practice.

@benjamingr
Copy link
Author

benjamingr commented May 16, 2020

@shaseley thank you for the response. I'm still not sure I understand entirely. I think a lot of these things you mentioned here (all of them?) have ad-hoc solutions (like isInputPending, requestPostAnimationFrame etc).

I am looking in particular for either:

  • A capability that this API adds that is impossible without it. For example a use case that this addresses and mixing and matching isInputPending / requestPostAnimationFrame / requestIdleCallback / setTimeout doesn't address.
  • An acknowledgement that this doesn't address any additional use cases but allows for mixing and matching different priorities in a way that requires "globality" - like an app embedded in another page and they need the scheduler to come from the platform so they can share priorities).
  • An acknowledgement that this doesn't actually add a capability but makes code a lot nicer to write since writing schedulers correctly is incredibly challenging (I've maintained the hacks we do at bluebird and it was pretty challenging at times).

To be clear, I am not objecting to this API in either of these three cases. I think this sort of API makes sense for the web platform anyway and I've found it incredibly useful in platforms that had it (like asp.net webapi servers). This sort of API (and controller) would also enable things like "schedule things off request stories" and actually can help quite a bit with seemingly unrelated things because it binds the tasks to a context (so for example the platform could use this for debugging info, context and scope).

@shaseley
Copy link
Collaborator

Hi, @benjamingr, apologies for the lag.

@shaseley thank you for the response. I'm still not sure I understand entirely. I think a lot of these things you mentioned here (all of them?) have ad-hoc solutions (like isInputPending, requestPostAnimationFrame etc).

I am looking in particular for either:

I don't think it's an either or here, but maybe a little (arguably) from (a), and a good deal from (b) and (c).

The API exposes three priorities that are used for two purposes:

  1. Coordination between all the things running on the page — 1P, 1P library, framework, 3P script. I believe this matches your second point below.
  2. For the browser to use for scheduling decisions, i.e. when selecting the next task to run from its various task queues. I think this has some overlap with your first and third points below (more below).

And it provides one guarantee: that user-blocking tasks run before user-visible tasks, which in turn run before background tasks. There are no ordering guarantees between postTask tasks and other tasks, although the priority should be used in decisions ((2) above).

  • A capability that this API adds that is impossible without it. For example a use case that this addresses and mixing and matching isInputPending / requestPostAnimationFrame / requestIdleCallback / setTimeout doesn't address.

'user-blocking' priority is probably the closest thing to a (performance) capability. To elaborate, you’re right that much of postTask in its current form can be polyfilled by these APIs:

  • postTask(foo, {priority: ‘background’}) maps pretty well to requestIdleCallback, and preserves the guarantee mentioned above.

  • postTask(foo, {priority: ‘user-visible’}) maps pretty well to setTimeout(foo, 0), with the caveat that setTimeout enforces a minimum delay after a certain nesting depth (which is why some developers have moved to postMessage).

For 'user-blocking' tasks, to get the same ordering guarantees as postTask, there are two options:

  1. Multiplex setTimeout (or whatever) to run both 'user-visible' and 'user-blocking' tasks. This is pretty much what userspace schedulers do, but there isn't a performance benefit with regards to other queued tasks (the browser can't tell the difference between the different setTimeouts).

  2. Use an API that has higher priority. The closest thing we have is to hijack rendering and use requestAnimationFrame, or requestPostAnimationFrame if it ships. But there are no ordering guarantees between rendering and non-rendering tasks (in Chromium, we’ve been working on increasing the priority of rendering, but there are trade-offs). These can also incur unnecessary overhead if not actually rendering, and they don't run in the background, forcing devs to change scheduling methods based on page visibility (both of which probably fall in the category of your third point).

There are also plans for building features on top of postTask which might be considered capabilities.

  1. Propagating scheduler context. The alternative is plumbing the signal everywhere.

  2. Integrating async APIs like fetch and IDB with TaskSignal (for priorities, etc.). The alternative now is to run short handlers that just queue tasks, but this doesn't help gain a higher priority.

  3. Controlling 3P script (very, very early stages).

  • An acknowledgement that this doesn't address any additional use cases but allows for mixing and matching different priorities in a way that requires "globality" - like an app embedded in another page and they need the scheduler to come from the platform so they can share priorities).
  • An acknowledgement that this doesn't actually add a capability but makes code a lot nicer to write since writing schedulers correctly is incredibly challenging (I've maintained the hacks we do at bluebird and it was pretty challenging at times).

I think both of these are important and are goals of the API. I'll acknowledge that the API is very polyfillable, but that performance characteristics will likely vary because of no direct mapping for 'user-blocking' (something we're trying to determine in testing / origin trial).

To be clear, I am not objecting to this API in either of these three cases. I think this sort of API makes sense for the web platform anyway and I've found it incredibly useful in platforms that had it (like asp.net webapi servers). This sort of API (and controller) would also enable things like "schedule things off request stories" and actually can help quite a bit with seemingly unrelated things because it binds the tasks to a context (so for example the platform could use this for debugging info, context and scope).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants