Skip to content
This repository has been archived by the owner on Jun 8, 2021. It is now read-only.

Has this seen adoption? #3

Open
annevk opened this issue Aug 11, 2017 · 24 comments
Open

Has this seen adoption? #3

annevk opened this issue Aug 11, 2017 · 24 comments

Comments

@annevk
Copy link
Member

annevk commented Aug 11, 2017

I'm asking since we still have https://bugzilla.mozilla.org/show_bug.cgi?id=686201 open, but the specification hasn't been maintained since 2011 and I'm not aware of other implementations.

@nolanlawson
Copy link
Member

It's adopted in Node, but I'm not aware of it being used in any non-Trident/EdgeHTML browsers.

@toddreifsteck
Copy link
Member

Node, IE and Edge are the only implementations. I believe it is fair to say that the spec needs work.

At the same time, there is massive adoption of setImmediate in the real world. I just checked and we see ~30% of navigations making use of it including Facebook, YouTube, Yahoo, Ebay, Roblox, many other Google properties and many Microsoft sites.

@foolip
Copy link
Member

foolip commented Oct 25, 2017

I came across https://w3c.github.io/setImmediate/ today and was wondering the same thing. Is there still hope that this will be implemented everywhere? If so, should there be web-platform-tests? If not, can the status of the spec be updated to something like https://w3c.github.io/geofencing-api/?

@toddreifsteck
Copy link
Member

25% of navigations in Microsoft Edge execute setImmediate including the following sites in the top 20: Facebook, YouTube, Google, Gmail, Bing, MSN, Ebay, GitHub.

The setImmediate spec has not been updated, because conversations about the hole it fills in prioritization/input blocking between microtasks and setTimeout(0) have generally been nonstarters. Tests have not been added for the same reason.

I'm happy to revive the discussions and potentially update the spec at any point.

@foolip
Copy link
Member

foolip commented Oct 25, 2017

So, in other words, this isn't on a path for removal in Edge, but expected to stay around and hopefully be picked up by others some time down the line?

@bmaurer
Copy link

bmaurer commented Nov 2, 2017

At FB we would love to see this api formalized. It's pretty common to have abstractions that want to do something "soon" for some definition of soon. For example waiting until the current slice of execution has finished to batch up multiple requests into a single request. I think we'd need to figure out exactly what rules we want to have around it, but I imagine we could come up with something useful

@foolip
Copy link
Member

foolip commented Nov 3, 2017

@annevk
Copy link
Member Author

annevk commented Nov 3, 2017

@bmaurer what's the definition of "soon" that you want though? "Queue a task" as defined here (plus potential delay apparently, looking at the algorithm), "queue a microtask", "run as next task", "run before next frame", "run after next frame", etc.

cc @domenic

@annevk
Copy link
Member Author

annevk commented Nov 3, 2017

cc @smaug---- @wanderview @farre

@smaug----
Copy link

I have the same question as @annevk in #3 (comment)
What do people actually do with setImmediate and why? Use of it may cause postponing rendering if heavy stuff is being done in the callback since it isn't controlled at all when it runs.

@wanderview
Copy link
Member

IMO exposing something closer to "post a task" that does not require allocating a MessageChannel would be useful. Its a primitive that sites use. Trying to force them into Promise or rAF seems a bit paternalistic to me.

At least based on recent discussions in the gecko bug we are in a better place to be able to implement it with some mitigations for abuse. We would need spec language to allow us to implement those mitigations. Marking setImmediate() a separate task source may be enough for that to work. (Ordering not guaranteed with anything else, etc.)

@domenic
Copy link

domenic commented Nov 3, 2017

My issue is that setImmediate is the least useful primitive among queueMicrotask, requestAnimationFrame, and requestIdleCallback. I have seen no legitimate use cases that aren't better covered by those three. (One "non-legitimate" use case is working around bugs in requestAnimationFrame, which apparently fires after render in two browsers.)

So I'd much rather prioritize implementing queueMicrotask and fixing requestAnimationFrame bugs, and performing appropriate outreach to get developers to switch to the more appropriate choice (e.g. by updating popular promise polyfills, or those top domains). Once we've done that we can reevaluate whether this primitive should be web-exposed, or whether it's too much of a footgun.

@toddreifsteck
Copy link
Member

queueMicrotask blocks input and the render steps till all of enqueued microtasks are complete. We are starting to see developers make mistakes by overqueueing these without understanding the impact. @jackarchibald will be going over this in an upcoming talk he is giving in an attempt to educate developers.

requestAnimationFrame is delayed until the render steps. This means that sites are giving up ~1-15ms of time if requestAnimationFrame is used to schedule a callback.

requestIdleCallback is designed for non-critical work and schedules the callback in the next frame. This is a GREAT API but has a different purpose than setImmediate.

setImmediate fills the hole between queueMicrotask and requestAnimationFrame by creating a "high priority" task source allowing a callback to be scheduled immediately while allowing a UA to prioritize an input task such as a link click to be processed.

setImmediate is very close to semantically equivalent to setTimeout(0) but prior research has found that setTimeout/setInterval(0/1) are widely and badly used and that all UAs have added heuristics to mitigate their usage. setImmediate does not seem to have fallen victim to the same issues as libraries seem to have been more thoughtful in adopting it.

We have some time on our schedule for the Web Performance Working Group on Tuesday for TPAC 2017 to review this issue. I'll add the specific scheduling slot to this issue after @igrigorik and I lock the time slot later today.

@wanderview
Copy link
Member

From our implementation standpoint queueMicrotask is more problematic because its easier to abuse and there is no mitigation strategy. Its a stealth sync loop and the only option is to throw up UX asking the user to stop the page.

Tasks like setImmediate() can be prioritized, yielded, etc among other work in extreme cases to avoid problems.

You also in spite of your assertions about no used cases I see web developers saying they have use cases:

https://twitter.com/youyuxi/status/915949863644418050
https://bugzilla.mozilla.org/show_bug.cgi?id=686201#c80
etc

@wanderview
Copy link
Member

We have some time on our schedule for the Web Performance Working Group on Tuesday for TPAC 2017 to review this issue.

Todd, I'd be interested in talking about setImmediate() next week, but I probably can't make this since SW meeting is also on Tuesday.

@domenic
Copy link

domenic commented Nov 3, 2017

Yes, queueMicrotask is indeed a sync loop; that's not an issue. I mean that it should be used in the promise libraries which are probably accounting for a large percentage of setImmediate usage.

I agree web developers say that they have use cases. In all cases (including the linked tweet) when we've drilled down we've found out they are confused about what tasks and microtasks mean, and when animation frame timing is.

High-priority tasks which you want to allow the UA to still do work before it happens should be done via requestIdleCallback with a timeout option indicating how soon you require execution.

@wanderview
Copy link
Member

I agree web developers say that they have use cases. In all cases (including the linked tweet) when we've drilled down we've found out they are confused about what tasks and microtasks mean, and when animation frame timing is.

I'll just say I disagree with your conclusions here. I don't think Evan or Kyle are confused about the difference between tasks and microtasks.

High-priority tasks which you want to allow the UA to still do work before it happens should be done via requestIdleCallback with a timeout option indicating how soon you require execution.

This is dramatically different than posting a task to the event queue.

The abuse mitigations I mentioned would be quite different than requestIdleCallback. My intent would be to design them so that well behaved sites don't encounter them at all. If the code is fine with potentially long delays then, sure, rIC is better. But its not a replacement for setImmediate().

Anyway, I don't see how exposing setImmediate(), which is already in a browser, would prevent developers from using promises, rAF, or rIC if they prefer those behaviors.

@domenic
Copy link

domenic commented Nov 3, 2017

rIC does not have potentially long delays if used with a timeout option.

@wanderview
Copy link
Member

rIC does not have potentially long delays if used with a timeout option.

So you are saying you think rIC with a timeout should escape the nested clamping we do for setTimeout(f, 0)? People who want setImmediate() behavior currently construct a MessageChannel to avoid this clamping.

Looking at our code I believe we enforce clamping on rIC timeouts.

@domenic
Copy link

domenic commented Nov 3, 2017

I assumed everything non-sync (so rIC and setImmediate both) would be subject to the clamping. If not now, they would be after a year when the abuse starts.

@wanderview
Copy link
Member

My exact point is we are in a position to implement setImmediate() without clamping, but also avoid abuse via prioritization/yielding under extreme conditions.

@domenic
Copy link

domenic commented Nov 3, 2017

I don't see why you wouldn't do the same for rIC then.

@wanderview
Copy link
Member

I don't see why you wouldn't do the same for rIC then.

We could in theory, but it would mean implementing a separate timer subsystem from setTimeout(). I don't really want to do that. Also, its unclear that would be something devs could rely on cross-browser.

I personally also have problems with rIC as a replacement for setImmediate() on conceptual grounds. Maybe this is just me, but I think about these primitives on a continuum:

  • Fastest/Sync: post a microtask, promise
  • Fast: post a task, setImmediate, postMessage
  • Fast/Medium: setTimeout(f, 0)
  • Medium/Painting related: requestAnimationFrame()
  • Slow/Don't care: requestIdleCallback()

If someone is calling requestIdleCallback(f, { timeout: 0 }), then I would have to ask why they are not just using setTimeout(f, 0). The intention of that call is weird and unclear. It simultaneously says "run this when you can and also run it as soon as possible".

Anyway, that's just me.

@domenic
Copy link

domenic commented Nov 6, 2017

What do you think the intention of setImmediate is?

Fast: post a task, setImmediate, postMessage

"post a task" is not a good example of this. It's intended for getting from the background thread ("in parallel") back to the main thread (event loop). It isn't a primitive meant for use from the main thread.

Similarly, there's a degenerate case where you postMessage to yourself to go from the main thread ... back to the main thread. That's not what postMessage is generally for; it's for going from one thread (or at least realm) to a different thread (realm).

So I don't think this point on the continuum is meaningful. If you want fast, you should want sync, and use microtasks.

Fast/Medium: setTimeout(f, 0)

Similarly, you shouldn't be using setTimeout as a "fast/medium" way of scheduling work. You should be using it for time-based scheduling. When I was a web developer, every instance of setTimeout(f, 0) was just working around our own poor coding (or sometimes a browser bug), or it was a poor-person's queueMicrotask.


In general I don't think you can meaningfully express "I want to do this work fast/medium/slow". My breakdown would be:

  • As soon as possible: microtask. Promises want this behavior, in particular, as they're just a value delivery mechanism, and are only not-sync because of plan interference attacks. (If you want to actually slow down delivery, then use one of the other mechanisms to delay calling resolve().)
  • In time for the next frame, i.e. anything visual: requestAnimationFrame
  • When this doesn't interrupt other important stuff like user input, optionally with a deadline: requestIdleCallback
  • A particular number of milliseconds in the future: setTimeout

I don't think there's a use case for setImmediate in here. So far the use cases that have been expressed have been along the lines of "I want this to happen soon but not block user input". That's exactly what requestIdleCallback is for.

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

No branches or pull requests

8 participants