New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.
Starred by 73 users
Status: WontFix
Owner: ----
Closed: Sep 2012
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Windows
Pri: 2
Type: Feature



Sign in to add a comment
Implement setImmediate/clearImmediate
Project Member Reported by phistuck@gmail.com, Sep 2 2012 Back to list
Can you, please, implement the Efficient Script Yielding specification?
http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html
 
Labels: -Type-Bug -Area-Undefined Type-Feature Area-WebKit WebKit-WebApps WebKit-JavaScript
Cc: arv@chromium.org
Comment 3 by arv@chromium.org, Sep 18 2012
Cc: tabatkins@chromium.org ojan@chromium.org rafaelw@chromium.org
Cc: jam...@chromium.org
@jamesr, Didn't this fail to gain any traction? Can we mark this "wontfix"
This is currently only implemented in Internet Explorer 10 -
http://msdn.microsoft.com/en-us/library/windows/apps/hh453394.aspx

Polyfills/shims/NodeJS packages that implement this -
https://github.com/NobleJS/setImmediate
https://npmjs.org/package/set-immediate
Comment 6 by jam...@chromium.org, Sep 18 2012
Status: WontFix
I don't think we should implement this.  Microsoft hasn't said a peep about it on the standards list and I haven't heard anything from other browser vendors.
I have to say there is a lot of interest in the web community for this. Mostly, those interested in promises implementations are looking for fast ways of executing stuff on the next tick of the event loop. Here are some examples:

https://github.com/NobleJS/setImmediate
https://gist.github.com/2802407
https://github.com/yui/yui3/pull/304

You can even see Douglas Crockford using it in his implemtation and talks: http://youtu.be/dkZFtimgAcM?t=41m57s

It'd be nice if this regained some interest.
Even if you guys don't like the spec, vendor prefix it while it's under discussion and I'll add the prefixed implementation to the setImmediate shim. That way, major promise implementations using the shim can be fast under Chrome as well as under IE10.
Is there anything specific you don't like about the spec as it currently stands?
Shim it as "setTimeout(..., 0)".  That will be the same effect as setImmediate in most places (we only clamp when timers are nested several layers deep).

I think most of the buzz on the web is because microsoft is really good at saying "hey look, this is faster!" repeatedly.  In practice, I haven't seen any examples of where this API provides a benefit that couldn't be achieved with the current web platform.
Is it important to the promise use case that control is yielded to the browser before the callback is run? If so, why?

If not, asyncCallback can be shim'd in a a few lines of code using the end-of-microtask callbacks of MutationObservers.
@jamesr

Shimming it with `setTimeout(..., 0)` makes it really really slow.  Check out this demmo: http://domenic.github.com/setImmediate-shim-demo/ which shows that even with decent shims it's still an order of magnitude slower on Chrome/Firefox than a native  implementation like the one on IE or in node.js.

@rafaelw

That should be sufficient to meet the promises spec.  I haven't come across a shim using it.  the `setImediate` shim from NobleJS does attempt a number of fall-backs though:

        if (canUseNextTick()) {
            // For Node.js before 0.9
            installNextTickImplementation(attachTo);
        } else if (canUsePostMessage()) {
            // For non-IE10 modern browsers
            installPostMessageImplementation(attachTo);
        } else if (canUseMessageChannel()) {
            // For web workers, where supported
            installMessageChannelImplementation(attachTo);
        } else if (canUseReadyStateChange()) {
            // For IE 6–8
            installReadyStateChangeImplementation(attachTo);
        } else {
            // For older browsers
            installSetTimeoutImplementation(attachTo);
        }
The reason why promises need to yield to the browser is that we want them to be predictably asynchronous. For example:

console.log(1);
promise.then(function () {
  console.log(3);
});
console.log(2);

...should always print 1 2 3, even if the promise is already resolved. We do that by forcing all callbacks to run in a timer of some sort.
Yes, but that just needs to yield to other JavaScript to be consistent, it doesn't need to yield all the way to the browser.  That is to say, It could run at the very end of the current event loop, instead of the start of the next one.
Correct. Using EOMT (MutationObservers) will accomplish the ensured ordered required in comment 13.

I would actually argue that in this case, EOMT is *preferable* to yielding to the browser since you have

-ensure async, but proper ordering of your work
-all of your async work gets to complete before the browser attempts to move on with business (such as painting -- which has desirable properties, like avoiding visual artifacts).
I agree that before-yielding behavior is more useful for our use case than after-yielding.

However, what we really desire is:

1) The ability to schedule something at the end of this tick (or at the beginning of the next tick, as the case may be) independent of a heavyweight mechanism like DOM mutation observations, cross-document messaging protocols, or future timer scheduling.

2) A single simple method libraries can use and reuse, without including ~40 lines of shim [1] necessary for implementing the desired semantics on top of the heavyweight mechanisms of 1).

3) Something as fast in the other browsers as setImmediate is in IE10. None of the mechanisms in 1) meet this criteria currently, as shown in the benchmarks at [2].

Given that no current solutions meet these desires, we're simply agitating for setImmediate because it observably does so, with IE10 as the proof. Mutation Observers fall down on all three, as do postMessage and setTimeout(,0).


[1]: https://github.com/tildeio/rsvp.js/blob/463e0a2528b60acdcc439e1d866af9081a9ede57/browser/rsvp.amd.js#L7-L49
[2]: http://domenic.github.com/setImmediate-shim-demo/
I'm not sure what to make of the previous argument.

(1) I don't know what "heavyweight" means. Perf? 

(2) Here's (roughly) the shim. It's not even close to 40 lines of code, and you've got a promise library to put it in anyway, right?

var hiddenDiv = document.createElement('div');
var callbacks = []
var observer = new WebKitMutationObserver(function(records) {
  var deliverCallbacks = callbacks.slice();
  callbacks.length = 0;
  deliverCallbacks.forEach(function(callback) { callback(); });
});

function scheduleAsyncCallback(callback) {
  if (!callbacks.length)
    hiddenDiv.setAttribute('foo', 'bar');
  callbacks.push(callback);
}

(3) What's you basis for this claim relative to Mutation Observers. There's no reason that the above shim would be noticeably slower than setImmediate on IE10.

Also, FWIW, I think it's reasonable to propose that the above be specified and implemented just for coherency of the platform, but a good path to that is first adopting the pattern and demonstrating its utility.
The pattern of the setImediate function is being adopted. The problem with your proposed shim is that it's WebKit only.  If we made it also support Firefox etc. then it becomes a lot more code.

The other problem with your code is that I can't see how it would actually implement the desired functionality?  How do you implement `setImediate` semantics on top of your proposed shim?

Say the code for my app is:

console.log('A');
scheduleAsyncCallback(function () {
  console.log('C');
});scheduleAsyncCallback();
console.log('B');

Then it only logs A then B (C is never actually logged).  Hence it appears not to work.
setImmediate threads always turn into a code-golf competition for who can come up with the shortest, buggiest implementation on top of existing web technologies. Let's not debate the specifics of comment 17's shim.

More substantive reply later; I just wanted to head off that tangent before it grew.
@ForbesLindesay Rafael's code is missing observer.observe(hiddenDiv, opts) which starts the whole thing. According to MDN, Firefox supports MutationObserver since version 14.

Working example: http://jsbin.com/agodad/2/edit

I still think we need a simple API for this. We shouldn't have to be using DOM observers for something completely unrelated.
Thanks, @ForbesLindesday. I was kind of hand-waving at the answer. Thanks for making it concrete.
> (1) I don't know what "heavyweight" means. Perf? 

That's the most important aspect. Other less important things include the conceptual burden of knowing that "use a mutation observer on a hidden DOM element" really means "schedule for end of turn," or "use a cross-document messaging protocol on yourself" really means "schedule for beginning of next turn."

> (2) Here's (roughly) the shim.

The biggest problem with (the corrected version of) this shim is that errors from one callback can interfere with others. Fixing this, without try/catch/rethrow (which ruins stack traces), requires either more trickery than I can think of right now, or much more overhead. 

The best approach I've got at the moment is pre-allocating a set of mutation observers/divs, to be reused each turn, and dynamically growing that set if someone calls `setImmediate` too many times in one turn. Then each callback gets its own mutation observer/div.

> (3) What's you basis for this claim relative to Mutation Observers. There's no reason that the above shim would be noticeably slower than setImmediate on IE10.

Indeed, after some preliminary testing with code that's not really fit to show to the public, mutation observers are quite fast in Chrome. Faster than IE10's setImmediate when run against the demo, although this is not quite fair because no painting occurs---leading to an immediate jump to the finished state with no animation in between. Thanks for the pointer.

---

But yes, after looking at mutation observers, I agree that if we could get some type of API that was like setImmediate but was end-of-turn instead of beginning-of-next-turn, that would be even more ideal. Nobody seems to have specced that up though :(
Wait, Kris Kowal showed me an exception-safe way to use a single queue:

https://github.com/montagejs/montage/blob/601f849e86150f427ebe49bef8317b3fcf4488f2/core/next-tick.js#L76-L96

So nevermind all that nonsense about a pool of mutation observers.
Project Member Comment 25 by bugdroid1@chromium.org, Mar 11 2013
Labels: -Area-WebKit -WebKit-WebApps -WebKit-JavaScript Cr-Content Cr-Content-JavaScript Cr-Content-WebApps
Project Member Comment 26 by bugdroid1@chromium.org, Apr 5 2013
Labels: -Cr-Content Cr-Blink
Project Member Comment 27 by bugdroid1@chromium.org, Apr 6 2013
Labels: -Cr-Content-JavaScript Cr-Blink-JavaScript
Cc: tonyg@chromium.org
 Issue 258482  has been merged into this issue.
 Issue 258482  has been merged into this issue.
We would also really love to get setImmediate to get rid of this monstrosity in the closure library https://code.google.com/p/closure-library/source/browse/closure/goog/async/nexttick.js

There has rarely been a clearer need for an API that is so easy to implement and is needed by pretty much everyone, yet missing from Chrome.

@malteubl You can use a MutationObserver to create a callback which will fire by the end of the current "microtask" (which is probably what you want here -- i doubt you actually want to spin the event loop and cause a paint before you run).

DOM Promises will probably have the same semantics, but they aren't available yet. Here's an example of something similar: https://github.com/rafaelw/ChangeSummary/blob/master/benchmark/benchmark.js#L35
@rafaelw: I know that I could make https://code.google.com/p/closure-library/source/browse/closure/goog/async/nexttick.js even longer. My point is that I want to get rid of all the browser specific hacks.

setImmediate in IE10 is really slow (faster than setTimeout(…, 0) but slower than postMessage), so it most certainly doesn't have the MutationObserver semantics.

We would prefer scheduling to the event loop over the MutationObserver semantics, so that the polyfills can have roughly equivalent semantics.
What is the goal of goog.async.nextTick()?  The comments on it are inaccurate and I don't see any example uses.
@rafaelw I think it'll be nice to have both. Hopefully ES7 will standardize a mechanism for scheduling something at the end of a microtask, but there will still be use cases for yielding to the browser.
@jamesr: The goal is essentially to emulate what the average user would expect setTimeout(…, 0)  to do just from looking at that code snippet.
Yes, but what are they trying to do?  More context is necessary.  Sometimes, doing setTimeout(..., 0) will fire it with no additional delay.  It all depends on what the author is actually trying to do and why.
There was an interesting blog entry today by Nicolas C. Zakas explaining
the need for setImmediate(), see it here:

http://www.nczonline.net/blog/2013/07/09/the-case-for-setimmediate/
We have 2 core use cases:

1. Guarantee async execution in our promise implementation
2. Allow pushing stuff to a queue during JS execution and then handling the pushed stuff after execution ends (i.e. by initiating a single HTTP request)

The additional delay of setTimeout is unacceptable. At the >90%tile the likelihood that other stuff happens before the delay finishes which may take arbitrary amounts of time makes the actual delay in the real world often much longer than what might have been intended by the browser implementer.
Also as a frequent variation of #2
Algorithms were N points in the program may set a dirty flag and having code that runs once per JS execution handle that flag.
Anything that causes a setTimeout to delay for arbitrary amounts of time will do the same to setImmediate so there's no advantage there.

Nicolas' post raises some interesting points, but deserves a longer response than will fit comfortably into a bug comment.
jamesr: While it is true that the same delays can happen in a very short time interval than in 5ms in the real world the likelihood is very different making the %ile in which it is perceived very different.

Just to finish this up: We have a real need for this API. It would be awesome to get it. Meanwhile we are happily using MessageChannel and iframes. If you're wearing the normal DOM-nastiness wetsuit that only makes you feel mildly dirty.
Comment 42 by get...@gmail.com, Jul 9 2013
(in reply to comment #40)

"Anything that causes a setTimeout to delay for arbitrary amounts of time will do the same to setImmediate..."

Except for the aforementioned 4ms clamping, right? The assumption is that setImmediate would have no such clamping.
Comment 43 by get...@gmail.com, Jul 11 2013
For those (from Chrome) who are saying (rather tongue-in-cheek) that setImmediate isn't needed because it can just be easily polyfilled:

1) Don't misleadingly call it a "polyfill" if you never intend for it to be standardized and accepted. "polyfill" is meant to suggest something that's standardized and that all/most modern browsers have it, and older legacy browsers need to be patched to provide an approximation. That's clearly NOT what you're suggesting here, in actuality.

Firstly, it's only in one browser. Secondly, it's not standard yet. I know it's "specified" by the Efficient Script Yielding spec, but AFAIK it's not progressing right now beyond draft status (in part) because it requires a second implementation (in addition to IE) and no other browser has agreed to do so yet. So, it's inaccurate to suggest to just polyfill something that you yourselves are actively blocking from becoming something that COULD in good faith be polyfilled once actually standardized.

It doesn't even sound like it's a prollyfill (probably-fill) at this point, by the tone of some of the comments from Chrome devs here. At best it's a hopefill (hopeful-fill) and we're just hacking around indefinitely. So, it's a hack. Call it what it is, until (if ever) we get something standardized.

So, you want us to *hack* something because you don't like what the Web Performance Working Group came up with in good faith, and/or you don't like how IE implemented it.

2) I question your judgement of what's a reasonable definition of "easy polyfill". Here's a project that implements a real "hack" to approximate the performance of setImmediate in other browsers:

https://github.com/NobleJS/setImmediate/blob/master/setImmediate.js

It uses `postMessage`, `MessageChannel`, and other such hacks, to accommodate best performance in all browsers and scenarios. I'm sure it's a very good lib and does what it purports to do, quite well.

So let's examine if it's easy and practical. It's 8.5k of expanded code. That right there should be an alarm. It's 3.2k of minified code. Ouch. And even when gzip'd, it's still over 1k. That's pretty hefty for a hack of a simple single-task feature.

I have a promises-like library that would need setImmediate (or some hack thereof) in it, and my library is 927 gzip'd bytes. That means that this little ol' "easy polyfill" hack is actually a fair bit bigger itself than the whole of my async flow control lib. That's just crazy.

Suggesting that a simple polyfill/hack for setImmediate is "enough" is nothing but plainly disingenuous (or ignorant of what that actually entails).

We may not be able to convince you that we need setImmediate built-in, but please stop hiding behind the convenient excuse that the polyfill/hack to obviate it is practical and advisable. There are clearly cases (like mine) where that's an absurd assertion.
My websites use setImmediate, falling back to setTimeout(func, 0). Any greater effort is not worthwhile to support browsers that are not interested in supporting new standards. Only one case existed where setTimeout was too slow to be reasonably useful, and in this case a notice is shown with a "please wait" message explaining that their current browser does not support new standards, and that IE10 is recommended.
FYI - setImmediate (and MessageChannel) is broken on IE10.  It's basically useless on IE10 Mobile, and somewhat unreliable on IE10 Desktop.  Just adding a spin.js spinner on our login page caused Q promises to stop working on IE10 Mobile, because it uses setImmediate.

I wrote a detailed description of the bug in my blog: http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/

> jamesr@chromium.org
> Shim it as "setTimeout(..., 0)".  That will be the same effect as setImmediate in
> most places (we only clamp when timers are nested several layers deep).

Actually, no it won't.  Since 2008, Chrome has promoted setTimeout(..,0) to setTimeout(...,1).  And it gets worse in Canary, where new bugs turn that into setTimeout(...,2)

And 'clamping' of timers has been busted in Chrome since 2008.  In many cases, setTimeout() will ALWAYS be clamped, even with no nesting.

Please file a bug with a test case with any issues you see.  Clamping works as expected on my build.
filed days ago...  Issue 331912 
get...@gmail.com, save your breath...

Given the nature of the clamping bug just found in  Issue 331912  -- and that Chrome has had this bug for OVER FIVE YEARS -- it is evident that Google has no test scripts to verify the integrity of Chrome timers (that they are unclamped in the beginning).

So when Google argues above that setTimeout(...,0) already does what setImmediate() does, that argument is fully contradicted by Chrome itself, and has been contradicted by Chrome for over five years.

That means NO ONE at Google actually tested the claims made above about setTimeout(...,0).  If Google had, the Chrome team would have figured out for themselves what everyone else already knows about setTimeout(...,0) in Chrome.

Google's argument is not based upon how Chrome actually works.  Instead, Google's argument is based upon hope and EMOTIONS.
Comment 51 by Deleted ...@, Apr 3 2014
Although I think its kind of hackish (to use DOM elements to do async calls).  MutationObserver's are the fastest method for timing immediate calls.

http://jsperf.com/setimmediate-test/10

According to IE11, they are even better to use than its own setImmediate, and provides a better response time.  The good thing is, that now they are cross browser for all major browsers according to the MDN page.

https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
jer...@duckware.com, did you even read the usernames or the earlier comments? About a year ago, an @google.com developer came begging because it would eliminate the need for a separate file filled with hacks to polyfill this functionality. At this point, the two main platforms that haven't implemented this, Firefox and Chrome (Node.js has support) have mostly abandoned the discussion.

If this needs discussed any more, please file a new bug or (preferably) ask the chromium-discuss@chromium.org mailing list.
Cc: alexclarke@chromium.org
Comment 54 Deleted
I can't believe this issue still hasn't been resolved. Either setTimeout(() => { ... }, 0); needs to work like setImmediate(() => { ... }) or you all need to implement setImmediate. As I see it the former breaks poorly developed websites, so setImmediate seems to be preferable. Regardless of which is correct, people are building serious programs in the browser, and making the argument that either isn't unnecessary, or is a foot cannon waiting to happen are both very bad arguments. I don't care if Timmy locks up chrome because he can't program. That's Timmy's problem. But I'm building a god dam rocket ship and I need proper tools to get the job done. As a side note there are already many APIs in the browser that can cause CPU thrashing. The clamping in setTimeout protects no one, it's simply annoying. You have to trust that there are engineers who know what they are doing. Stop trying to protect engineers from themselves, we can figure it out on our own.

TL;DR. Please pick setTimeout(() => { ... }, 0) without clamping or setImmediate(() => { ... }). We don't need you to protect us from ourselves, get it done.
Comment 56 by phistuck@gmail.com, Apr 13 2016
While it was not implemented as setImmediate and friends, something similar was implemented -
requestIdleCallback

See https://developers.google.com/web/updates/2015/08/using-requestidlecallback for details.

requestIdleCallback is a good addition. However it is not a replacement for setImmediate. Yielding to the browser is not desirable here.
In that case you should not use setImmediate, since it yields to the browser.
I'd be very interested to hear of any real world use cases that a real setTimeout 0 would enable.
alexclarke/59: I am working on a new project where an entire database is being downloaded to the client.  On a filter change, there is a time consuming computation.  I can either (1) just do the computation and lock up the GUI, (2) break it up into components (crazy easy to do) and use setTimeout(fn,0) but then get penalized with unnecessary delays introduced by Chrome in when 'fn' is being called back, (3) use workers (not practical in this project), or (4) use requestIdleCallback (but the computation needs to run at high priority, just not locking up the gui; not ultra low priority behind everything else) or (5) use a polyfill.

A real work use, where I was forced to use a polyfill.  Sad.

@alexclarke we use it as a poor-man's profiler — we want to delay the metrics finishing until after the browser has flushed our DOM changes. (when I say "it", we currently use the post-message over a MessageChannel hack).
@phistuck: requestIdleCallback() is not related to setImmediate() in any way - it's actually a pretty *slow* async-erator (it can delay an arbitrary amount of time, until the browser feels like it's "idle").

@conrad: There are multiple ways of getting DOM changes to "flush" besides yielding to the event loop.  In particular, querying styling information tends to do that.

@robertwhurst: As Domenic said, *everything* async yields to the browser.  All setImmediate() (or the related hacks that approximate it) would do is put the callback into the event queue *immediately*, rather than delaying for a little bit first.  If anything was already queued up behind it, or got queued while the previous code was executing, it'll still run before the setImmediate() callback.
Comment 63 by phistuck@gmail.com, Apr 13 2016
@tabatkins -
Well, it is a little similar, really. Note that (while it might not be documented) setImmediate is not guaranteed to ever be called back (and there are (or were) actually cases where it never does). I might be piggybacking on implementation bugs, but I still want to make that point. :P
Yes, those are just bugs - setImmediate() is exactly as guaranteed to be called as setTimeout(), since it's just `setTimeout(x=>..., 0-but-i-really-mean-it)`. ^_^
@jerryj unfortunately a polyfill is your best best for now. If I was writing one I'd be   tempted to do something like this:

Each "frame" race a requestAnimationFrame and a requestIdleCallback. Which ever one wins, you now have an idea of when the browser needs you to yield the thread (say 8-10ms after the raf timestamp or deadline from requestIdleCallback).  Then run a loop executing tasks until you have exceeded the deadline.  As long as tasks are chunked up nicely (i.e. everything is <50ms and ideally less) the browser should remain nice and responsive if it's compositor scrolling.  If there are scroll handlers you'd want the tasks to be all less than 10ms or so.

If you're executing a lot of small tasks it's usually good to avoid too many round trips from JS to C++ and back again due to the overhead (unless of course you actually need that for correctness).
@alex, the recurring usecase for me comes from long JS tasks. The ReactNative team has expressed this challenge as well.

There's a big task that's like 1000-2000ms of JS on the main thread. (e.g. templating out DOM, initializing handlers for a server-side rendered page, etc) 

We don't want the user to take the main thread for that long of time. (So we can handle any new high priority input, etc). So we want the task to yield, but developers will only yield if they are sure that the delay is as small as possible and there will NOT be waste time rendering the partial work so far.


There are rough plans to help that specific use-case (doing a lot of DOM work without janking the UI thread) that should show up in an incubator group soon!
Comment 68 by d3c...@gmail.com, May 17 2017
Was really surprised setImmediate was not available in Chrome since it is available in Node.

I use it because setTimeout(cb,0) is at least 1ms; limiting tick rate to 1000.  setImmediate() I can easily do 10-20 ticks per millisecond, extending the throughput drastically.




Comment 69 by phistuck@gmail.com, May 17 2017
#68 - look into requestIdleCallback -
https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
Sign in to add a comment