Differencebetween Settimeout(Fn, 0) and Settimeout(Fn, 1)

What is the difference between setTimeout(fn, 0) and setTimeout(fn, 1)?

I think the answer is "It depends" now.

We can run the code in different platform and browsers:

function setTimeouts() {
setTimeout(function() { console.log(2); }, 2);
setTimeout(function() { console.log(1); }, 1);
setTimeout(function() { console.log(0); }, 0);
}

for (var i = 0; i < 10; i++) {
setTimeouts();
}

NodeJS - setTimeout(fn,0) vs setImmediate(fn)

setTimeout is simply like calling the function after delay has finished. Whenever a function is called it is not executed immediately, but queued so that it is executed after all the executing and currently queued eventhandlers finish first. setTimeout(,0) essentially means execute after all current functions in the present queue get executed. No guarantees can be made about how long it could take.

setImmediate is similar in this regard except that it doesn't use queue of functions. It checks queue of I/O eventhandlers. If all I/O events in the current snapshot are processed, it executes the callback. It queues them immediately after the last I/O handler somewhat like process.nextTick. So it is faster.

Also (setTimeout,0) will be slow because it will check the timer at least once before executing. At times it can be twice as slow. Here is a benchmark.

var Suite = require('benchmark').Suite
var fs = require('fs')

var suite = new Suite

suite.add('deffered.resolve()', function(deferred) {
deferred.resolve()
}, {defer: true})

suite.add('setImmediate()', function(deferred) {
setImmediate(function() {
deferred.resolve()
})
}, {defer: true})

suite.add('setTimeout(,0)', function(deferred) {
setTimeout(function() {
deferred.resolve()
},0)
}, {defer: true})

suite
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').pluck('name'));
})
.run({async: true})

Output

deffered.resolve() x 993 ops/sec ±0.67% (22 runs sampled)
setImmediate() x 914 ops/sec ±2.48% (57 runs sampled)
setTimeout(,0) x 445 ops/sec ±2.79% (82 runs sampled)

First one gives idea of fastest possible calls. You can check yourself if setTimeout gets called half as many times as other. Also remember setImmediate will adjust to your filesystem calls. So under load it will perform less. I don't think setTimeout can do better.

setTimeout is un-intrusive way of calling functions after some time. Its just like its in the browser. It may not be suited for server-side (think why I used benchmark.js not setTimeout).

Why is setTimeout(fn, 0) sometimes useful?

In the question, there existed a race condition between:

  1. The browser's attempt to initialize the drop-down list, ready to have its selected index updated, and
  2. Your code to set the selected index

Your code was consistently winning this race and attempting to set drop-down selection before the browser was ready, meaning that the bug would appear.

This race existed because JavaScript has a single thread of execution that is shared with page rendering. In effect, running JavaScript blocks the updating of the DOM.

Your workaround was:

setTimeout(callback, 0)

Invoking setTimeout with a callback, and zero as the second argument will schedule the callback to be run asynchronously, after the shortest possible delay - which will be around 10ms when the tab has focus and the JavaScript thread of execution is not busy.

The OP's solution, therefore was to delay by about 10ms, the setting of the selected index. This gave the browser an opportunity to initialize the DOM, fixing the bug.

Every version of Internet Explorer exhibited quirky behaviors and this kind of workaround was necessary at times. Alternatively it might have been a genuine bug in the OP's codebase.


See Philip Roberts talk "What the heck is the event loop?" for more thorough explanation.

How is timing of `setImmediate()` and `setTimeout()` bound by the performance of the process?

The actual trick is in the Timeout constructor which is called by setTimeout and which increases times below 1 to 1. Thus setTimeout(fn, 0) is actually equivalent to setTimeout(fn, 1).

When libuv initializes, it starts with timers after updating its internal clock, and when one millisecond already passed till then it's gonna pick up the timer before proceeding to the poll phase (which is followed by the setImmediate phase).


Another interesting observation is that multiple timers might also run before and after setImmeadiate:

setTimeout(() => console.log('timer'), 1);
setTimeout(() => console.log('timer'), 1);
setImmediate(() => console.log('immediate'));
// can produce:
// timer
// immediate
// timer

That's because setTimeout calls getLibuvNow internally, which will call env->GetNow() and which not only gets the current time of libuv, but also updates it. Thus it might happen that the timer get put into the timer queue with different due times, and thus the timer phase will only pick up some of them.

OK, but what makes so-called "I/O cycles" different from the main module?

The main module gets run before libuv is initialized, whereas most other code will run in the poll phase of the libuv loop. Thus the main module initialization is followed by the timer phase, whereas the poll phase is followed by the 'check handles' phase, which among others runs the setImmediate callbacks. Thus usually in the main module timers would run before immediates (if they're due) and if scheduled in callbacks, immediates would run before timers.



Related Topics



Leave a reply



Submit