I am writing a node.js library that requires setup in order to run. I have used setTimeout to show a warning if the setup function isn't run within 5 seconds.
However, this makes the program wait for 5 seconds before exiting, even if all the other code is finished.
I want to show a warning message iff the setup function hasn't been called and the program is still running after 5 seconds. Is there a way to do this with vanilla JS?
const done = new Event(); // Event is a simple class I made for a one-time event multicast
function setup() {
done.emit();
}
const timeoutHandle = setTimeout(() => showWarning(), 5000);
done.subscribe(() => {
clearTimeout(timeoutHandle);
});
console.log("The program should end here");
// The program keeps going for 5 seconds even after the last statement
However, this makes the program wait for 5 seconds before exiting, even if all the other code is finished.
You can utilize timeout.unref():
When called, the active Timeout object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, the process may exit before the Timeout object's callback is invoked. Calling timeout.unref() multiple times will have no effect.
So const timeoutHandle = setTimeout(() => showWarning(), 5000); timeoutHandle.unref();
This will prevent the setTimeout to keep the process active.
Related
I'm using MIDI.js to play a MIDI file with several musical instruments.
The following things execute too late, how can I fix that?
First notes of the song. Like all notes, they are scheduled via start() of an AudioBufferSourceNode here.
MIDI program change events. They are scheduled via setTimeout here. Their "lateness" is even worse than that of the first notes.
When I stop the song and start it again, there are no problems anymore, but the delay values are very similar. So the delay values are probably not the cause of the problem.
(I use the latest official branch (named "abcjs") because the "master" branch is older and has more problems with such MIDI files.)
That is how JavaScript Event Loop works.
Calling setTimeout ... doesn't execute the callback function after the given interval.
The execution depends on the number of waiting tasks in the queue.
... the delay is the minimum time required for the runtime to process the request (not a guaranteed time).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#zero_delays
Instead of setTimeout() you can use window.requestAnimationFrame() and calculate elapsed time for delay by yourself.
Window.requestAnimationFrame() - Web APIs | MDN
The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint.
... will request that your animation function be called before the browser performs the next repaint. The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
performance.now() - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
In our situation, we don't want to do any animation but want to just use it for a better-precision timeout.
const delayMs = 1000;
const startTime = performance.now();
function delay(func) {
const delayStartTime = performance.now();
function delayStep() {
// Run again if still elapsed time is less than a delay
if (performance.now() - delayStartTime <= delayMs) {
window.requestAnimationFrame(delayStep);
}
else
{
// Run the delayed function
func();
}
}
// Run first time
window.requestAnimationFrame(delayStep);
}
// Trying `setTimeout()`
setTimeout(() => doSomeJob('setTimeout()'), delayMs);
// Trying `delay()`
delay(() => doSomeJob('delay()'));
// Function that we'd like to run with a delay
function doSomeJob(marker)
{
const elapsedTime = performance.now() - startTime;
console.log(`${marker}: Ran after ${elapsedTime / 1000} seconds`);
}
If you run it many times, you'll see that delay() is pretty much all the time better than setTimeout(). The difference is very small because there is nothing else happens on the page. If there will be something intensive running, setTimeout() should demonstrate worse "precision".
The following example is given in a Node.js book:
var open = false;
setTimeout(function() {
open = true
}, 1000)
while (!open) {
console.log('wait');
}
console.log('open sesame');
Explaining why the while loop blocks execution, the author says:
Node will never execute the timeout callback because the event loop is
stuck on this while loop started on line 7, never giving it a chance
to process the timeout event!
However, the author doesn't explain why this happens in the context of the event loop or what is really going on under the hood.
Can someone elaborate on this? Why does node get stuck? And how would one change the above code, whilst retaining the while control structure so that the event loop is not blocked and the code will behave as one might reasonably expect; wait
will be logged for only 1 second before the setTimeout fires and the process then exits after logging 'open sesame'.
Generic explanations such as the answers to this question about IO and event loops and callbacks do not really help me rationalise this. I'm hoping an answer which directly references the above code will help.
It's fairly simple really. Internally, node.js consists of this type of loop:
Get something from the event queue
Run whatever task is indicated and run it until it returns
When the above task is done, get the next item from the event queue
Run whatever task is indicated and run it until it returns
Rinse, lather, repeat - over and over
If at some point, there is nothing in the event queue, then go to sleep until something is placed in the event queue or until it's time for a timer to fire.
So, if a piece of Javascript is sitting in a while() loop, then that task is not finishing and per the above sequence, nothing new will be picked out of the event queue until that prior task is completely done. So, a very long or forever running while() loop just gums up the works. Because Javascript only runs one task at a time (single threaded for JS execution), if that one task is spinning in a while loop, then nothing else can ever execute.
Here's a simple example that might help explain it:
var done = false;
// set a timer for 1 second from now to set done to true
setTimeout(function() {
done = true;
}, 1000);
// spin wait for the done value to change
while (!done) { /* do nothing */}
console.log("finally, the done value changed!");
Some might logically think that the while loop will spin until the timer fires and then the timer will change the value of done to true and then the while loop will finish and the console.log() at the end will execute. That is NOT what will happen. This will actually be an infinite loop and the console.log() statement will never be executed.
The issue is that once you go into the spin wait in the while() loop, NO other Javascript can execute. So, the timer that wants to change the value of the done variable cannot execute. Thus, the while loop condition can never change and thus it is an infinite loop.
Here's what happens internally inside the JS engine:
done variable initialized to false
setTimeout() schedules a timer event for 1 second from now
The while loop starts spinning
1 second into the while loop spinning, the timer is ready to fire, but it won't be able to actually do anything until the interpreter gets back to the event loop
The while loop keeps spinning because the done variable never changes. Because it continues to spin, the JS engine never finishes this thread of execution and never gets to pull the next item from the event queue or run the pending timer.
node.js is an event driven environment. To solve this problem in a real world application, the done flag would get changed on some future event. So, rather than a spinning while loop, you would register an event handler for some relevant event in the future and do your work there. In the absolute worst case, you could set a recurring timer and "poll" to check the flag ever so often, but in nearly every single case, you can register an event handler for the actual event that will cause the done flag to change and do your work in that. Properly designed code that knows other code wants to know when something has changed may even offer its own event listener and its own notification events that one can register an interest in or even just a simple callback.
This is a great question but I found a fix!
var sleep = require('system-sleep')
var done = false
setTimeout(function() {
done = true
}, 1000)
while (!done) {
sleep(100)
console.log('sleeping')
}
console.log('finally, the done value changed!')
I think it works because system-sleep is not a spin wait.
There is another solution. You can get access to event loop almost every cycle.
let done = false;
setTimeout(() => {
done = true
}, 5);
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('event loop');
resolve();
})
);
}
const run = async () => {
while (!done) {
console.log('loop');
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
Node is a single serial task. There is no parallelism, and its concurrency is IO bound. Think of it like this: Everything is running on a single thread, when you make an IO call that is blocking/synchronous your process halts until the data is returned; however say we have a single thread that instead of waiting on IO(reading disk, grabbing a url, etc) your task continues on to the next task, and after that task is complete it checks that IO. This is basically what node does, its an "event-loop" its polling IO for completion(or progress) on a loop. So when a task does not complete(your loop) the event loop does not progress. To put it simply.
because timer needs to comeback and is waiting loop to finish to add to the queue, so although the timeout is in a separate thread, and may indeed finsihed the timer, but the "task" to set done = true is waiting on that infinite loop to finish
var open = false;
const EventEmitter = require("events");
const eventEmitter = new EventEmitter();
setTimeout(function () {
open = true;
eventEmitter.emit("open_var_changed");
}, 1000);
let wait_interval = setInterval(() => {
console.log("waiting");
}, 100);
eventEmitter.on("open_var_changed", () => {
clearInterval(wait_interval);
console.log("open var changed to ", open);
});
this exemple works and you can do setInterval and check if the open value changed inside it and it will work
I am using javascript to send a bench of request at regular interval (every 5 ms).
I tried to use setTimeout and also sleep function, but none of them have accurate timing.
They ensure that the time interval is >= 5ms but not == 5ms.
Any idea?
It seems that this very difficult to achieve in javascript or even impossible!!
This is the code I am using:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sendRequest(){
var i;
for (i=1; i<= numberOfRequests; i++){
// send my i^th request here
await sleep(5);
}
}
There isn't any way to provide exact timeouts in any programming language as much as they live in a general purpose multiprogrammed operating system. That happens because the exact moment the operating system will give its time slice to a particular process is just unpredictable.
Furthermore, JavaScript is single-threaded and it works with an event loop system, and the asyncrhonous tasks (such as setTimehout, xhr callback, click listeners and so on) will be executed only after that all the current code is finished. For example, if you have:
setTimeout(() => console.log('hello world'), 500);
for (let i = 0; i<1E100; i++) {
console.log(Math.sqrt(i));
}
Hello world will be only printed only after all the calculations are completed.
Since javascript uses a single threaded event loop there is no way to obtain that accuracy. The events in the event loop are executed when the engine has finished the previous task. If you set a task to be executed after 20 ms (setTimeout, setInterval or any other custom task) the engine will add that task to the event loop then it will try to run any other task from the loop (let's assume a function that takes 25 ms to run). Since javascript is single thread, you can start any other element from the loop, until the 25ms task is finished. In that case, your timeout will start after 25 ms, even if you set it to 20. This is how javascript architecture works.
Even if you implement multi thread (workers, threads etc.) the event loop will still be present in each of them (each thread has its own loop)
Curious to see whether setTimeout() will be fired up asynchronously, I tried the following test script:
function timedText() {
var x = document.getElementById("txt");
setTimeout(function() {
x.value = "1 second"
}, 1000);
setTimeout(function() {
x.value = "2 seconds"
}, 2000);
setTimeout(function() {
x.value = "3 seconds"
}, 3000);
while (true) {}
}
<p>Click on the button below. The input field will tell you when two, four, and six seconds have passed.</p>
<button onclick="timedText()">Display timed text</button>
<input type="text" id="txt">
Sure enough, clicking the button causes the browser to hang.
This tells me that setTimeout() does not run on a separate thread.
But on a recent interview, the interviewer suggested otherwise... Does that mean that setTimeout() is browser/implementation dependent?
JavaScript is not multi threaded. Well there are WebWorkers that run in a different thread, but that's not really multi threading, more like multiple processes, that communicate with each other.
As of that the while (true) {} will block the js context, because it is an endless loop.
The setTimeout will register a function for later execution. But at no time code will run in parallel for the same context.
A while (true) itself does not necessarily create a blocking loop:
async function sleep(time) {
return new Promise((resolve, _) => setTimeout(resolve, time))
}
async function test(val) {
while (true) {
console.log('in while loop ' + val)
await sleep(1000)
}
}
test('foo')
test('bar')
So you can say with await/async you can create some kind of cooperative multitasking like setup, but still no multi threading
There is no thread in javascript.
setTimeout push just the delegate function insto a stack that will pop for the next pass.
You can read that JavaScript and Threads
This tells me that setTimeout() does not run on a separate thread.
Yes. There is only one thread in JS.
But on a recent interview, the interviewer suggested otherwise... Does
that mean that setTimeout() is browser/implementation dependent?
As far as i know only engine changed from browser to browser. Internal mechanism stands the same - event-loop processor.
When you call setTimeout() typically control is passing back into the host environment (the browser or native node.js code for example). What is happening then is that your callback is being registered in a list of timers to execute in the future. setTimeout() will the return back to your code which will continue executing.
When your script finally completes, control again will return to the host environment which has an event loop, this loop keeps spinning until it's finally time to call your registered callback.
You can actually approximate something like this in JavaScript itself by implementing an event loop just for fun:
class EventLoop {
constructor() {
this.entries = []; // a list of all registered callbacks
this.turns = 0; // keep track of how many turns of the loop we make
}
// Adds a new callback to the list
schedule(callback, condition) {
this.entries.push([condition, callback]);
}
// To removes a callback when it's been called
remove(entry) {
this.entries.splice(this.entries.indexOf(entry), 1);
}
// Run the loop until all registered callbacks were called
// Returns the number of turns it made in the while loop
run(timeout) {
this.turns = 0;
while (this.entries.length) {
for (const entry of this.entries) {
const [condition, callback] = entry;
if (condition()) {
callback();
this.remove(entry);
}
}
this.turns++;
}
return this.turns;
}
}
We can use this EventLoop to implement something like a setTimeout():
// Define a handy log function
const log = ((startTime) => (text) => {
console.log(`t+${(Date.now() - startTime).toFixed(3)}ms: ${text}`);
})(Date.now());
// Create an event loop
const loop = new EventLoop();
// Define a setTimeout using the event loop
const defer = (fn, timeout) => {
const start = Date.now();
const end = start + timeout;
loop.schedule(fn, () => Date.now() >= end);
};
// Schedule some nested events
defer(() => {
log('I run second');
defer(() => {
log('I run third');
defer(() => {
log('I run fourth');
}, 200);
}, 200);
}, 200);
// Log syncronously
log('I run first');
// Start the event loop turning (blocks until all events are complete)
const turns = loop.run();
log(`Loop exited after ${turns} turns`);
// This will log after event loop has finished running
log('I run last');
If you run this with node.js you'll get the following output:
t+0.000ms: I run first
t+200.000ms: I run second
t+400.000ms: I run third
t+600.000ms: I run fourth
t+600.000ms: Loop exited after 6441157 turns
t+600.000ms: I run last
We just created an asynchronous timeout in pure JavaScript with a single thread. Now in reality you wouldn't do this in JavaScript, the event loop would be implemented in native code and hosted in the host environment. An example of such an event loop is libuv used by Node.js. Libuv can do things more efficiently than our toy JS example, it can put the current thread to sleep (technically it doesn't do this, it polls for IO but same concept) so it's not wasting CPU cycles.
Those asynchronous functions are handled by the browser. not the JavaScript engine. there are no threads in JavaScript.
I have a jQuery Mobile web app which targets iOS and Android devices. A component of the application is a background task, which periodically checks for a.) changes to local data and b.) connectivity to the server. If both are true, the task pushes the changes.
I'm using a simple setTimeout()-based function to execute this task. Each failure or success condition calls setTimeout() on the background task, ensuring that it runs on 30 second intervals. I update a status div with the timestamp of the last task runtime for debugging purposes.
In any desktop browser, this works just fine; however, on iOS or Android, after some period of time, the task stops executing. I'm wondering if this is related to the power conservation settings of the devices--when iOS enters stand-by, does it terminate JavaScript execution? That is what appears to happen.
If so, what is the best way to resume? Is there an on-wake event which I can hook into? If not, what other options are there which don't involve hooking into events dependent on user interaction (I don't want to bind the entire page to a click event just to restart the background task).
Looks like Javascript execution is paused on MobileSafari when the browser page isn't focused. It also seems if setInterval() events are late, they are simply fired as soon as the browser is focused. This means we should be able to keep a setInterval() running, and assume the browser lost/regained focus if the setInterval function took much longer than usual.
This code alerts after switching back from a browser tab, after switching back from another app, and after resuming from sleep. If you set your threshold a bit longer than your setTimeout(), you can assume your timeout wouldn't finish if this fires.
If you wanted to stay on the safe side: you could save your timeout ID (returned by setTimeout) and set this to a shorter threshold than your timeout, then run clearTimeout() and setTimeout() again if this fires.
<script type="text/javascript">
var lastCheck = 0;
function sleepCheck() {
var now = new Date().getTime();
var diff = now - lastCheck;
if (diff > 3000) {
alert('took ' + diff + 'ms');
}
lastCheck = now;
}
window.onload = function() {
lastCheck = new Date().getTime();
setInterval(sleepCheck, 1000);
}
</script>
Edit: It appears this can sometimes trigger more than once in a row on resume, so you'd need to handle that somehow. (After letting my android browser sleep all night, it woke up to two alert()s. I bet Javascript got resumed at some arbitrary time before fully sleeping.)
I tested on Android 2.2 and the latest iOS - they both alert as soon as you resume from sleep.
When the user switches to another app or the screen sleeps, timers seem to pause until the user switches back to the app (or when the screen awakens).
Phonegap has a resume event you can listen to instead of polling for state (as well as a pause event if you want to do things before it is out of focus). You start listening to it after deviceReady fires.
document.addEventListener("deviceready", function () {
// do something when the app awakens
document.addEventListener('resume', function () {
// re-create a timer.
// ...
}, false);
}, false);
I use angular with phonegap and I have a service implemented that manages a certain timeout for me but basically you could create an object that sets the timer, cancels the timer and most importantly, updates the timer (update is what is called during the 'resume' event).
In angular I have a scopes and root scope that I can attach data to, my timeout is global so I attach it to root scope but for the purpose of this example, I'll simply attach it to the document object. I don't condone that because you need should apply it to some sort of scope or namespace.
var timeoutManager = function () {
return {
setTimer: function (expiresMsecs) {
document.timerData = {
timerId: setTimeout(function () {
timeoutCallback();
},
expiresMsecs),
totalDurationMsecs: expiresMsecs,
expirationDate: new Date(Date.now() += expiresMsecs)
};
},
updateTimer: function () {
if (document.timerData) {
//
// Calculate the msecs remaining so it can be used to set a new timer.
//
var timerMsecs = document.timerData.expirationDate - new Date();
//
// Kill the previous timer because a new one needs to be set or the callback
// needs to be fired.
//
this.cancelTimer();
if (timerMsecs > 0) {
this.setTimer(timerMsecs);
} else {
timeoutCallback();
}
}
},
cancelTimer: function () {
if (document.timerData && document.timerData.timerId) {
clearTimeout(document.timerData.timerId);
document.timerData = null;
}
}
};
};
You could have the manager function take a millisecond parameter instead of passing it into set, but again this is modeled somewhat after the angular service I wrote. The operations should be clear and concise enough to do something with them and add them to your own app.
var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
You will want to update the timer once you get the resume event in your event listener, like so:
// do something when the app awakens
document.addEventListener('resume', function () {
var manager = timeoutManager();
manager.updateTimer();
}, false);
The timeout manager also has cancelTimer() which can be used to kill the timer at any time.
You can use this class github.com/mustafah/background-timer based on #jlafay answer , where you can use as follow:
coffeescript
timer = new BackgroundTimer 10 * 1000, ->
# This callback will be called after 10 seconds
console.log 'finished'
timer.enableTicking 1000, (remaining) ->
# This callback will get called every second (1000 millisecond) till the timer ends
console.log remaining
timer.start()
javascript
timer = new BackgroundTimer(10 * 1000, function() {
// This callback will be called after 10 seconds
console.log("finished");
});
timer.enableTicking(1000, function(remaining) {
// This callback will get called every second (1000 millisecond) till the timer ends
console.log(remaining);
});
timer.start();
Hope it helps, Thank you ...
You should use the Page Visibility API (MDN) which is supported just about everywhere. It can detect if a page or tab has become visible again and you can then resume your timeouts or carry out some actions.