4 ms·
The situation caused by promises gaining popularity and non-IE browsers refusing to implement setImmediate is really quite ugly. setTimeout's minimum value is d
by exogen 12y ago
The situation caused by promises gaining popularity and non-IE browsers refusing to implement setImmediate is really quite ugly. setTimeout's minimum value is defined to be 4ms by the HTML5 spec, so if the browser conforms to that and your app makes heavy use of promises (using setTimeout), now you're waiting multiple screen refreshes for updates – so you don't actually want to use setTimeout. Even though the asynchronous requirement is good for developers using the promise API, it really screwed over implementors.
The setImmediate polyfills attempt a bunch of ugly fallbacks, including (depending on which one you use): yield/generators, MutationObserver, postMessage/MessageChannel, onreadystatechange, and finally setTimeout (the slowest).
IMO the browser vendors should just give in and add setImmediate – if ever there was a need for it, this is it. But maybe they consider ES6 features a good enough substitute, I don't know.
- cpeterso 12y agoHere's the Firefox bug about implementing (or not) setImmediate: https://bugzilla.mozilla.org/show_bug.cgi?id=686201 https://bugzilla.mozilla.org/show_bug.cgi?id=686201
- lhorie 12y agoThis. Ran into this very issue when I was implementing promises in Mithril ( http://lhorie.github.io/mithril http://lhorie.github.io/mithril ). This really makes promise performance a much uglier beast than it needs to be. I took a less conventional road there: I deliberately violate the A+ spec and let surprises like the ones in the article example happen, in order to get good performance without hacks/bloat. Typically, promise resolution is usually done asynchronously anyways (otherwise, why bother using promises?), so this caveat would only be a problem in some serious case of misuse. In any case, surprises are fixable in application space (i.e. just move lines of code around), but performance degradation due to some deep implementation detail in the promise API / browsers not playing ball is nearly impossible to fix unless you switch to a non-conformant promise library (in which case, you'd have to fix the surprises anyways - assuming you had any cases of misuse to begin with).
- spion 12y agoA+ doesn't require that you use the browser's asynchronous scheduling mechanism, it only requires that the promise callback is always executed after the current function. There is a really neat hack to achieve this without completely relying on the browser's scheduler by implementing your own callback queue. asap(callback); which will push to an array of callbacks that you need to execute and then call a loop if one isn't currently active: function asap(cb) { callbacks.push(cb); setTimeout(flushQueue, 5); } Flushqueue will run all the callbacks from the queue in a loop, then empty it function flushqueue() { if (flushing) return; flushing = true; for (var k = 0; k < callbacks.length; ++k) { callbacks[k](); } flushing = false; } Now if some of the callbacks schedules a new function to execute using asap promise.then(function first(x) { return alreadyResolvedPromise.then(function second(y) { return y.some.property; }); }); then when "promise" resolves, first()'s execution will push a new function to the queue, changing queue.length, causing that loop above to cause one more iteration - but only after first is complete. (If you don't nest the callbacks, then the push will happen before flushqueue even starts executing) The code above is overly simplified but I think its enough to demonstrate the idea. (Huge thanks Stefan Penner for explaining it to me :D)
- exogen 12y agoUnfortunately there's no way to remove the need for flushQueue being called with the browser's asynchronous scheduling mechanism (using setTimeout in your example) in the case where it isn't already flushing – that's the issue. Flushing the callback queue to completion is an optimization, not really a fix.
- spion 12y agoYes, truly asynchronous things which took less than 5ms will now take at least 5ms to execute. But its a stretch to say that for Promises/A+ implementations "... always require at least one more iteration of the event loop to resolve. This is not necessarily true of the standard callback approach." as the article states. A fully synchronous chain of 20 operations wont take 100ms - it will only take 5ms
- pornel 12y ago> IMO the browser vendors should just give in and add setImmediate Firefox has just shipped native promises, and you can use them to polyfill setImmediate instead ;) http://kangax.github.io/es5-compat-table/es6/#Promise http://kangax.github.io/es5-compat-table/es6/#Promise
- masklinn 12y agoPromises are being moved to native implementations, so the lack of setImmediate isn't really a problem anymore.
- tav 12y agoWhilst the concept of setImmediate is nice, the current implementation in IE isn't properly integrated with the rest of the event loop — resulting in broken behaviour when you use it in combination with setTimeout [1], DOM events, etc. In contrast, using MutationObserver results in correct behaviour on all modern browsers and relatively minimal delays — between 0.002ms and 0.007ms according to an OS X only micro-benchmark I did last month [2]. And, yes, it would be great if we could call a builtin instead of hacking on top of MutationObserver, but it isn't that ugly: if MutationObserver $div = root.document.createElement 'div' observer = new MutationObserver tick observer.observe $div, attributes: true scheduleTick = -> $div.setAttribute 'class', 'tick' return else scheduleTick = -> setTimeout tick, 0 return In conclusion, I agree that a feature like setImmediate would be great. But given IE's broken implementation and a viable workaround in modern browsers, I see no need to rush it. I'd rather they focused on: new features like Object.observe; improving the performance of old features like Object.seal; and finalising some of the ES7 ideas like exposing the event loop! [1] http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ http://codeforhire.com/2013/09/21/setimmediate-and-messagech... [2] https://gist.github.com/tav/9719011 https://gist.github.com/tav/9719011
- domenicd 12y agosetImmediate works with macrotasks; promises work with microtasks. These are very different. See e.g. https://github.com/YuzuJS/setImmediate#macrotasks-and-microtasks https://github.com/YuzuJS/setImmediate#macrotasks-and-microt... Also, note that yield/generators are entirely synchronous, and cannot be used as a scheduling mechanism of any sort.