I'm trying to write a Javascript function that calls doSomething() continuously, as long as some_condition is true. If some_condition becomes false, it should check after five seconds whether some_condition has again become true, and if not, display an error message on the screen.
I currently have this much of the function written, but am confused about whether I am doing this correctly. Thanks for the help.
while (some_condition) {
doSomething();
else {
???
}
No, you should not use a while loop. JavaScript is single threaded, and if you have a while loop that never returns, the browser (or your JavaScript environment of choice) will become unresponsive.
You need to use setTimeout to schedule the function.
function enqueueSomething() {
if (some_condition)
doSomething();
// Enqueue ourselves to run again without blocking the thread of execution
setTimeout(enqueueSomething);
else
// Wait 5 seconds, then try again
setTimeout(enqueueSomething, 5000);
}
You'll need to maintain some state that indicates whether you've previously waiting 5 seconds, and add your error-alerting code.
Related
I am running a very basic javascript code in Viusal Studio Code. However I am not seeing the callback function get triggered.
I am trying to understand how setTimeout() works in javascript. I am running the following code (in file called test.js) in Visual Studio code as follows:
node test.js
'use strict'
let timerexpired = false;
setTimeout( function() {console.log(`setting timerexpired to true...`);
timerexpired = true;
},
5000);
while(1) {
console.log(`timerexpired = `, timerexpired);
if (timerexpired) {
console.log(`timer expired. Resume execution`);
break;
} else {
console.log(`keep on spinning`);
}
}
Expect the loop to break after 5000 ms. But it keeps on spinning with output as "keep on spinning"
When you call setTimeout or when you do any blocking operation for that matter, it's in fact added to an event table and not executed immediately.
It's only after the call stack is empty that the event loop check the event table to see if there's anything waiting, and move it to the call stack.
To better understand this, check the following example:
setTimeout(() => console.log('first'), 0)
console.log('second')
You may think that first will be logged before second, but in fact it's second that will be logged first.
In your example the call stack is never empty, so setTimeout won't really work as expected.
You may wanna check this, it will give you a better idea about what's happening under the hood.
While(1) is an infinite while loop. I think that is the reason
One of my javascript function is processing millions of data and it is called ~1 time every second from a hardware event. Then the web browser is idle in that function processing.
I tried to set a flag for running (or not running) that function:
if (!is_calculating)
is_calculating = true;
else
return;
my_function(); // do heavy stuff
is_calculating = false;
but it's not working, because it is entering into the code and the web browser enter in an idle status until is finishing. When it is returning, the flag is always OK, because it finished the // do heavy stuff
Can I do something for this behavior? I'd like to jump function execution if a flag is set.
The problem is, by default javascript runs in a single thread on browsers, so your code is executing completely before it even begins to process the next call, resulting in is_calculating always being false when the function is called. One workaround you could use (not the cleanest solution in the world), is to divide your monolithic 'heavy stuff' function into a number of smaller functions and have them call each other with setTimeout(nextFunc, 1). Having them call each other that way gives the browser a moment to do what it needs to do, and additionally call your function again if that's what it's doing. This time, because your function is called in the 'middle' of it already being executed, is_calculating is still going to be true, and the call will return at the beginning like you expect it to. Note this solution probably isn't as preferable as the Web Workers solution, but it is simpler.
function sleep(millis) {
var date = new Date()
var curDate = null
do { curDate = new Date() }
while(curDate-date < millis)
}
function reallyLong() {
if(!reallyLong.flag) {
reallyLong.flag = true
} else {
console.log("Not executing")
return
}
sleep(250)
setTimeout(reallyLong2, 1)
function reallyLong2() {
sleep(250)
setTimeout(reallyLong3, 1)
}
function reallyLong3() {
sleep(250)
setTimeout(reallyLong4, 1)
}
function reallyLong4() {
sleep(250)
console.log("executed")
reallyLong.flag = false
}
}
If you define all your consecutive functions inside the primary function, it also allows them all to access the same data simply and easily.
The only catch now is if your function was returning some value, you need to rewrite it to either return a promise (Either of your own design or using a library like Q), or accept a callback as a parameter that the last function in the 'chain' will call with the return value as a parameter.
Note that the sleep function above is a hack, and awful, and terrible, and should never be used.
By default JavaScript execution in browsers is not concurrent. This means, usually there can be only one currently executing piece of code.
You have to use Web Workers API to make you code run concurrently.
I was trying to move an image by adding to its .style.left and all that worked, until I tried to add a while cycle (to add until its left value is 90) and add setTimeout so that the movement is visible. Problem is that the HTML doesn't even load anymore and after a while it asks me to either Stop script or Continue - both of which help nothing and I am forced to kill the browser.
Here's the relevant code:
function sprint(){
button = parseInt(document.getElementById("azisButton").style.left.split("%")[0]);
while(button<10){
setTimeout(function(){
button++;
//console.log(button);
}, 1000);
//console.log("Tady");
}
}
Why does it happen? Do I get into infinite loop, or something?
EDIT: Is there any way to do it some other, smart way?
You built an endless loop! button++ is incremented in your callback, but the callback is only executed when the loop ends, as JavaScript is single threaded.
The while construct is a synchronous operation. Therefore you're just adding to the heap till the browser runs out of memory. What you need is a self-invoking function.
Best way to imagine this is to think of setTimeout as an IOU or debt system. Imaging the task you made with the while loop as a basket. You're job is to write an IOU and place it in the basket as a "to do later" or "do this task when I'm done with the task of the while loop"
So as you iterate over the loop each setTimeout becomes a "to do later" IOU and the while loop goes to the next iteration. Since the incrementing task is in one of those "to do later" functions they never get executed because they are waiting for the while loop to finish first. Hence the infinite loop.
To fix this logic you have to break out of the loop all together and make your own loop logic using a self-invoking function:
var button = 0;
function next() {
button++;
if (button < 10) {
setTimeout(next, 1000);
}
}
next();
A good way to keep this strait in your head is to interchange the setTimeout with a console.log in your mind. With a console.log you expect the command to return immediately and move on. The function passed into setTimeout will wait till the parent function it is defined in to finish before calling the function you passed it. In the example above next() is called after the parent next() finishes.
That is an infinite loop, since Javascript is single-threaded, none of your callbacks execute until the while loop finishes, which never happens, because button isn't incremented until the callbacks execute.
function sprint(){
button = parseInt(document.getElementById("azisButton").style.left.split("%")[0]);
if(button > 90){
// move button code here
setTimeout(sprint, 1000);
}
}
I'd like to be able to dispatch a bunch of work via JavaScript to be done in the browser in such a way that the browser stays responsive throughout.
The approach I'm trying to take is to chunk up the work, passing each chunk to a function that is then queued with a setTimeout(func, 0) call.
I need to know when all the work is done, so I'm storing the returned timer ID in a map (id -> true|false). This mapping is set to false in the next block of code after I have the timer ID, and the queued function sets the mapping to true when it completes... except, of course, the queued function doesn't know its timer ID.
Maybe there's a better/easier way... or some advice on how I can manipulate my map as I need to?
I would queue the work in an array, use one timeout to process the queue and call a callback once the queue is empty. Something like:
var work = [...];
var run = function(work, callback) {
setTimeout(function() {
if(work.length > 0) {
process(work.shift());
setTimeout(arguments.callee, 25);
}
else {
callback();
}
}, 25);
};
run(work, function() {
alert('Work is done!');
});
As JavaScript in browsers is single threaded there is no real advantage to run multiple timeouts (at least I think this is what you are doing). It may even slow down the browser.
I'd like to add that although javascript is single threaded you can still have multiple ajax calls going at once. I recently had a site that needed to do potentially hundreds of ajax calls and the browser just couldn't handle it. I created a queue that used setTimeOut to run 5 calls at once. When one of the ajax calls returned it fired a callback (which is handled by a single thread) and then made the next call on the stack.
Imagine you're a manager that can only talk to one person at a time, you give 5 employees assignments, then wait for their responses, which may come in any order. Once the first employee comes back and gives you the information, you give them a new assignment and wait for the next employee (or perhaps even the same employee) to come back. So although you're "single threaded" 5 things are going on at once.
There is an example right in the HTML Standard, how it is best to handle it:
To run tasks of several milliseconds back to back without any delay,
while still yielding back to the browser to avoid starving the user
interface (and to avoid the browser killing the script for hogging the
CPU), simply queue the next timer before performing work:
function doExpensiveWork() {
var done = false;
// ...
// this part of the function takes up to five milliseconds
// set done to true if we're done
// ...
return done;
}
function rescheduleWork() {
var handle = setTimeout(rescheduleWork, 0); // preschedule next iteration
if (doExpensiveWork())
clearTimeout(handle); // clear the timeout if we don't need it
}
function scheduleWork() {
setTimeout(rescheduleWork, 0);
}
scheduleWork(); // queues a task to do lots of work
The moment of finishing the work is pretty clear, when clearTimeout is called.
Say that I have the following code:
function testA {
setTimeout('testB()', 1000);
doLong();
}
function testB {
doSomething();
}
function doLong() {
//takes a few seconds to do something
}
I execute testA(). I have read that Javascript is single-threaded. What happens after 1000 milliseconds, when the timeout for testB() is reached?
Some possibilities I can think of:
testB() is queued up to execute after doLong() and anything else it called have finished.
doLong() is immediately terminated and testB() is started.
doLong() is given a little while longer to execute before being stopped (either automatically or after prompting the user) and testB() is started.
doLong() is paused, testB() is started. After testB() has finished, doLong() resumes.
What is the correct answer? Is it implementation dependant or part of the standard?*
This question is similar but not the same, as far as I can tell.
Any links that you can recommend for better understanding Javascript execution would be appreciated.
Thanks!
*Yes, I know that not all browsers follow standards :(
The first of your guesses is the correct one:
testB() is queued up to execute after doLong() and anything else it called have finished.
If it takes more than one second for testA to finish, testB will simply have to wait.
Also, you should write setTimeout(testB, 1000) rather than setTimeout('testB()', 1000). Sending a string to setTimeout is, like using eval, generally considered evil and will make you enemies ;)