@webos/timers
Synopsys
Abstraction layer on setTimeout
and setInterval
, implementation of setImmediate
.
Introduction
There are six functions in this library.
sleep
(milliseconds: optional (default 0)) -> Reusable Lazy Promise
Usage
sleep(3000).then(_ => console.log('Log ::: After ~3000 milliseconds'));
const sleep_five_seconds = sleep(5000);
sleep_five_seconds.then(_ => console.log('Log ::: After ~5 seconds'));
sleep_five_seconds.then(...)
sleep
uses setTimeout
wrapped with reusable lazy promise
( when 0 or nothing is passed, it calls setImmediate
instead of setTimeout
);
nextTick
() -> Reusable Lazy Promise
Usage
nextTick().then(_ => console.log('Log ::: immediately'));
const next_tick = nextTick();
next_tick.then(_ => console.log('Log ::: immediately'));
next_tick.then(...);
nextTick
uses setImmediate
wrapped with reusable lazy promise.
every
(fn: required, milliseconds: optional (default 0), iterations: optional (default Infinity)) -> Reusable Lazy Promise
Usage
every(_ => console.log('Log ::: every ~500 milliseconds'), 500, 20).then(_ => console.log('Log ::: I have finished'));
const log_every_ten_milliseconds = every(_ => console.log('Log ::: every ~10 milliseconds'), 500, 4);
log_every_ten_milliseconds.run();
log_every_ten_milliseconds.stop();
log_every_ten_milliseconds.run();
every
uses setInterval
wrapped with reusable lazy promise.
So, it means that every next call of fn will be after inputted time. ( When execution time of fn is not bigger than inputted time ).
For example, let's consider that we have an application of every
like this.
every(fn, 500, 4).then(...)
And, let's imagine that the execution time of fn is ~100 milliseconds. In this case it will work like this.
As we see, each next function will be called after 400 milliseconds from the end of the previous function.
If you want to call every next function after inputted time plus execution time of function you can use loopDelayBetween
instead of every
.
loopDelayBetween
(fn: required, milliseconds: optional (default 0), iterations: optional (default Infinity)) -> Reusable Lazy Promise
Usage
loopDelayBetween(_ => console.log('Log ::: every ~500 milliseconds'), 500, 20).then(_ => console.log('Log ::: I have finished'));
const log_every_ten_milliseconds = every(_ => console.log('Log ::: every ~10 milliseconds'), 500, 4);
log_every_ten_milliseconds.run();
log_every_ten_milliseconds.stop();
log_every_ten_milliseconds.run();
Unlike every, loopDelayBetween
uses recursive setTimeout
or setImmediate
( when inputted time is 0 ) instead of setInterval
.
So, it will call every next function after previous function execution time plus inputted time.
For example, suppose that we have an application of loopDelayBetween
like this.
loopDelayBetween(fn, 500, 4).then(...)
And, let's imagine that execution time of fn is ~100 milliseconds. In this case it will work like this.
setImmediate
MDN - This method is used to break up long running operations and run a callback function immediately after
the browser has completed other operations such as events and display updates.
The setImmediate
problem is that it's not part of any specification and is not supported by many browsers.
MDN
setImmediate
is similar to setTimeout(..., 0)
. But setTimeout
have minimum timeout (~4ms).
It means that when we call setTimeout(..., 0)
it will work after ~4ms.
MDN
Historically browsers implement setTimeout() "clamping": successive setTimeout() calls with delay smaller than the "minimum delay" limit are forced to use at least the minimum delay.
The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5ms.
In fact, 4ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.
In addition to "clamping", the timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks.
To implement a 0 ms timeout in a modern browser, you can use window.postMessage() as described here.
Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed Integer internally. This causes an Integer.
There are some implementations of this, like setZeroTimeout or setImmediate.
The best polyfill of setImmediate
is this polyfill. They use postMessage
, MessageChannel
, even "script onreadystatechange" for reaching maximum support in old browsers.
In this library, the implementation of setImmediate
uses Promise.resolve()
.
It's the fastest way, but writing setImmediate
polyfill by using Promise.resolve()
has one problem.
It's the following: Promise
polyfill uses setImmediate inside, if it exists.
promise-polyfill
So, if we write setImmediate
polyfill without using checkings, cyclic calls will occur.
For avoiding this the implementation of setImmediate
in this library checks if the browser supports native Promise
,
If so, it uses Promise.resolve()
, otherwise uses setTimeOut(..., 0)
. Can I use... says that ~90% of all browsers support native Promise
.
So, in nearly 90% of all browsers this implementation of setImmediate
will work much more faster than any other implementation.
But, of course, in 10% of browsers setTimeout(..., 0)
will work.
====
(fn: required) -> timerID
Usage
const timerID = setImmediate(fn)
clearImmediate
(timerID) -> Boolean
Usage
const timerID = setImmediate(fn);
clearImmediate(timerID);
License
MIT