Why use setTimeout with 0ms instead of any setInmediate variant in Tracker?


#1

Hi all,

Sorry if this question may be a bit stupid, but I have been looking at the internals of Tracker to understand how it works, and I was wondering why a setTimeout 0 was used on the invalidate to trigger the Tracker.flush instead of some variant of setIntermediate like https://github.com/YuzuJS/setImmediate or https://github.com/kriskowal/asap.

According to their docs:

ASAP’s task queue immediately at the end of the current event loop turn, before any rendering or IO (in browsers supporting microtasks) while setImmediate will yield for IO, reflow, and repaint events. It also returns a handler and can be canceled.

If I didn’t miss anything, it seems that this could be an improvement on reactiveness ot flush after invalidation and maybe improve performance for Blaze templates.

Again, maybe this is a no-issue, so please fell free to correct me. I will try to create some sample test app to test if there is any performance gain in doing so.

Best regards
Sergio


#2

Looks like one of the libraries you linked is using process.nextTick: https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js#L60.

To my knowledge nextTick exists only in node but Tracker usually runs in a browser? Also it looks like it does setTimeout 0 as well.


#3

Not sure why we don’t just use Meteor.defer here (which uses Meteor._setImmediate, which uses either setImmediate, some message-posting thing, or setTimeout depending on what is available).


#4

According to asap documentation it uses a variety of techniques depending on which browser is it run or if run on node:

    The following browsers allow the use of DOM mutation observers to access the HTML microtask queue, and thus begin flushing ASAP's task queue immediately at the end of the current event loop turn, before any rendering or IO:

Android 4–4.3
Chrome 26–34
Firefox 14–29
Internet Explorer 11
iPad Safari 6–7.1
iPhone Safari 7–7.1
Safari 6–7
In the absense of mutation observers, there are a few browsers, and situations like web workers in some of the above browsers, where message channels would be a useful way to avoid falling back to timers. Message channels give direct access to the HTML task queue, so the ASAP task queue would flush after any already queued rendering and IO tasks, but without having the minimum delay imposed by timers. However, among these browsers, Internet Explorer 10 and Safari do not reliably dispatch messages, so they are not worth the trouble to implement.

Internet Explorer 10
Safair 5.0-1
Opera 11-12
In the absense of mutation observers, these browsers and the following browsers all fall back to using setTimeout and setInterval to ensure that a flush occurs. The implementation uses both and cancels whatever handler loses the race, since setTimeout tends to occasionally skip tasks in unisolated circumstances. Timers generally delay the flushing of ASAP's task queue for four milliseconds.

Firefox 3–13
Internet Explorer 6–10
iPad Safari 4.3
Lynx 2.8.7

Also, the setInmediate shim does somehting similar:

The setImmediate API, as specified, gives you access to the environment's task queue, sometimes known as its "macrotask" queue. This is crucially different from the microtask queue used by web features such as MutationObserver, language features such as promises and Object.observe, and Node.js features such as process.nextTick. Each go-around of the macrotask queue yields back to the event loop once all queued tasks have been processed, even if the macrotask itself queued more macrotasks. Whereas, the microtask queue will continue executing any queued microtasks until it is exhausted.

In practice, what this means is that if you call setImmediate inside of another task queued with setImmediate, you will yield back to the event loop and any I/O or rendering tasks that need to take place between those calls, instead of executing the queued task as soon as possible.

#5

Right, I found it later on. I think that Tracker should use at least Meteor.defer instead of the setTimeout 0.
It also seems that Meteor implementation of setInmediate is a bit simple and the other alternatives provide a better solution for the majority of browsers.


#6

Comments in the source say it’s designed to work without Meteor (therefore, no Meteor.defer).