Coder Social home page Coder Social logo

Promise-based API about scheduling-apis HOT 15 OPEN

wicg avatar wicg commented on May 26, 2024 4
Promise-based API

from scheduling-apis.

Comments (15)

shaseley avatar shaseley commented on May 26, 2024 7

Using a promise here comes with two issues:

One is the lack of cancellation. While in the naive case, it's more ergonomic, in fully flushed out app, there would be a need for boolean cancellation guards and throwing errors to reject the async function to ensure promises down stream are appropriately notified of the cancellation. The ergonomics are then lost.

One suggestion here would be to have these methods accept a cancellation token. But it's still not ideal, from an ergonomics perspective, IMO. As you're forced to handle the cancellation-related rejections in downstream consumers, but that's more of a problem with async await.

The API supports cancellation through TaskControllers (and TaskSignals), which extend AbortController (and AbortSignal), the TAG-approved way of canceling async operations. This is described in the section on controlling tasks and illustrated in this example.

Another issue I see with using a promise here is forced asynchrony. That might sound odd, but if someone wanted to create a userland scheduler with the same API, they could not create one that was able to execute tasks in the same event. In RxJS, one of our schedulers is a "queueScheduler" which is used to ensure reactive events are executed "breadth first". Ideally RxJS would be able to adopt this scheduler, but if promises are used the forced async would make it impossible.

There is of course an argument to be made about the usefulness of the RxJS queueScheduler to begin with, though.

I think there are two concerns here, both asynchronicity of the API and desired task order that supports your use case? I think these are actually separable, IIUC.

Asynchronicity: The API is inherently asynchronous since tasks are queued and run at a later time, which is a fundamental part of the API. The main goal of this API is to coordinate tasks across the entire system, using a shared notion of priority to make scheduling decisions, and relying on a degree of cooperative multitasking to enable scheduling opportunities (i.e. task boundaries). Our task model includes multiple sources of work — framework code; 1P code; 3P code; I/O, which we may eventually want to add prioritization to — all of which flows through the scheduler. The native scheduler’s primary job is to select the next task from these task sources and run it, which it does asynchronously (in the turn of the event loop).

Whether postTask returns a Promise to represent the result of the deferred work (as per TAG design principles), or accepts a callback that is run when the function runs, it still happens asynchronously.

(After) Task timing: IIUC the particular scheduler/model that you’re describing queues up work to be run at the end of a single task, which is quite different from the particular problem we’re trying to solve (at the moment) with postTask.

That said, this is something that is (as of recently) on our radar. We initially had a render-blocking priority (originally called “immediate”) that would essentially run immediately after the current task finished (but still asynchronously). We removed it from the explainer because of a lack of use cases, but were presented with some recently while discussing the API with a Frameworks and Libraries group. The use case is that several frameworks flush work synchronously in a microtask, e.g. after a (VDOM) rendering pass. Being able to do that with postTask would be appealing in order to integrate it with TaskControllers/TaskSignals, enabling reprioritization. Would this help your use case?

Additionally, I'd note that even attempting to make this API ergonomic is probably a bad goal. Most user land code should never touch this API. If they do need to, then the framework authors, browser vendors, and runtime developers have failed pretty hard. Frameworks and some libraries like RxJS or possibly Redux et al, may have use for this, but such projects should be willing to take on some complexity if it affords greater flexibility. IMHO, I'd drop the use of promises here and aim for callbacks or something more simplified that doesn't force as much behavior as promise does.

I disagree; we’ve been talking to frameworks, site authors, and site authors that use frameworks, all of whom are interested in better scheduling and using the API. We think the API has potential beyond just frameworks and libraries (though they’re an important piece), and that ergonomics and ease-of-use are important for greater adoption.

Also, the API shape isn’t just about ergonomics. The API shape evolved quite a bit to get to the current Controller/Signal-based shape (see this issue), which IMO is more consistent with the platform, enables better integration with other async work (i.e. shared AbortSignal), and has the potential to enable dynamic reprioritization of diverse async work (eventually).

from scheduling-apis.

domenic avatar domenic commented on May 26, 2024 2

I'll poke my head in to note that non-user-accessible propagation would likely build on the same spec-level primitives we would need to solve incumbent propagation at the host layer (see whatwg/html#5213, including Boris's description of how Gecko implements things in whatwg/html#5213 (comment), and the last paragraph of whatwg/html#5213 (comment)). That is, some kind of host hook that is called whenever an async callback is passed to an ECMAScript API, and potentially others that are called before/after that async callback gets invoked.

So yeah, I don't think this primitives exist very rigorously now, but we probably do want to add them at some point.

from scheduling-apis.

benlesh avatar benlesh commented on May 26, 2024 2

Additionally, I'd note that even attempting to make this API ergonomic is probably a bad goal. Most user land code should never touch this API. If they do need to, then the framework authors, browser vendors, and runtime developers have failed pretty hard. Frameworks and some libraries like RxJS or possibly Redux et al, may have use for this, but such projects should be willing to take on some complexity if it affords greater flexibility. IMHO, I'd drop the use of promises here and aim for callbacks or something more simplified that doesn't force as much behavior as promise does.

from scheduling-apis.

shaseley avatar shaseley commented on May 26, 2024 1

Hi folks, FYI the API shape has changed considerably since the earlier TaskQueue-based proposal. Partially because of TAG feedback (see this issue), we've moved to a controller-based design, and postTask also now returns a Promise.

@littledan thanks for the feedback, the yield idea inspired another related API we're considering to solve the yield and continuation problem.

Any additional comments on the ergonomics of new postTask API shape are welcome and appreciated!

from scheduling-apis.

shaseley avatar shaseley commented on May 26, 2024 1

We don't have any JavaScript syntax changes in mind at present, but I think it's possible we'd explore that in the future.

The main thing that comes to mind is an alternative to the scheduler.yield proposal: It might be neat there was a yieldy JS primitive that could yield control back to the browser to process higher priority work (maybe a yieldy generator?). This could make yielding more ergonomic and potentially more efficient (not clear), but there might be some layering issues to contend with. But, we haven't thoroughly thought this through yet.

The other thing that is somewhat related is scheduling context propagation. To make this work we need to restore context when microtasks run, which has involved digging into V8. But, this doesn't require syntax changes and isn't exposed to developers (aside from behavior). I think we already have all of the hooks we need to spec this, but this is still TBD.

from scheduling-apis.

benlesh avatar benlesh commented on May 26, 2024 1

Using a promise here comes with two issues:

One is the lack of cancellation. While in the naive case, it's more ergonomic, in fully flushed out app, there would be a need for boolean cancellation guards and throwing errors to reject the async function to ensure promises down stream are appropriately notified of the cancellation. The ergonomics are then lost.

One suggestion here would be to have these methods accept a cancellation token. But it's still not ideal, from an ergonomics perspective, IMO. As you're forced to handle the cancellation-related rejections in downstream consumers, but that's more of a problem with async await.

Another issue I see with using a promise here is forced asynchrony. That might sound odd, but if someone wanted to create a userland scheduler with the same API, they could not create one that was able to execute tasks in the same event. In RxJS, one of our schedulers is a "queueScheduler" which is used to ensure reactive events are executed "breadth first". Ideally RxJS would be able to adopt this scheduler, but if promises are used the forced async would make it impossible.

There is of course an argument to be made about the usefulness of the RxJS queueScheduler to begin with, though.

from scheduling-apis.

Jack-Works avatar Jack-Works commented on May 26, 2024 1

Hello everyone, I have a question about the API. Why do you re-invent a "yield" instead of using the native async generators?

This is the code in the example:

async function doWork() {
  while(true) {
    let hasMoreWork = doSomeWork();
    if (!hasMoreWork) return;
    await scheduler.yield();
  }

Why not

async function * doWork() {
  while(true) {
    let hasMoreWork = doSomeWork();
    if (!hasMoreWork) return;
    yield;
}
schduler.schdule(doWork())

from scheduling-apis.

shaseley avatar shaseley commented on May 26, 2024 1

@Jack-Works We're actually working on a proposal for a more narrowly scoped yield that does exactly this and integrates with scheduler.postTask (i.e. postTask tasks can be made 'yieldy' with generators). I'll follow up with a separate issue when we've fleshed out the details a bit more.

from scheduling-apis.

benlesh avatar benlesh commented on May 26, 2024 1

I think there are some misnomers in here about asynchrony. Promises force a specific type of asynchronous behavior. It's possible to write asynchronous code that executes before a promise can resolve. A promise-based API for scheduling could not possibly model that.

Perhaps we just want this API to be thennable? But that comes with expectations. This API just needs to have some sort of escape hatch to allow behaviors other than promise-forced scheduling. For testing. For allowing same-shaped queue-scheduling, etc.

Abort[Signal/Controller] is a fine cancellation tool for this API, I think. Even if it's not the best design overall for general use cases, it's great to have it in the platform.

My general concern, as someone that would adopt this scheduling API, and push it to millions of users, is that the API is able to model scheduling that happens faster than promise resolution can occur.

from scheduling-apis.

littledan avatar littledan commented on May 26, 2024

Ping on this thread, since @ojanvafai mentioned in conversation that there was still some thinking in the direction of some kind of additional syntax for this purpose.

from scheduling-apis.

esprehn avatar esprehn commented on May 26, 2024

fwiw we have something like this in our internal codebase to avoid mega-tasks. Having it built into the task queue API would be nice.

const nextTask = (value) => new Promise(resolve => setTimeout(() => resolve(value)));

which we use to break up expensive promise waterfalls:

doSomething()
  .then(nextTask)
  .then(anotherThing);

from scheduling-apis.

littledan avatar littledan commented on May 26, 2024

The new API shapes look interesting to me, and I'm glad to see that they don't require any syntax changes. Is anyone still considering proposing JavaScript syntax changes for scheduling? If not, I'd be fine with closing this issue.

from scheduling-apis.

littledan avatar littledan commented on May 26, 2024

Leaving open these paths of investigation sounds good to me; looking forward to hearing about

The general idea of propagating information through async contexts has a lot of related work, e.g., the TC39 Zones proposal (which has been stalled for a long time, partly due to implementation concerns from Chromium engineers). There's also interest in a slightly more specific mechanism in Node.js. cc @bmeurer @domenic @jeisinger @gsathya . I can understand if user-accessible propagation of information is too complicated/hard-to-implement/undesirable, though; built-in propagation seems simpler.

from scheduling-apis.

shaseley avatar shaseley commented on May 26, 2024

/cc @natechapin who is working on implementing the scheduling context propagation.

from scheduling-apis.

Jack-Works avatar Jack-Works commented on May 26, 2024

Cool, is there a link for that?

from scheduling-apis.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.