I'm trying to return a variable only after it has been set within a setTimeout callback. I couldn't think of any other way to do this, but here's my attempt (I know the code seems silly, but it's to work around a Cordova bug). For reasons beyond my understanding, it results in an infinite loop.
function isConnected() {
var done = false;
setTimeout(function() {
done = true;
}, 500);
while (!done) {}
return navigator.connection.type!== Connection.NONE;
}
Can anyone explain to me why this is happening, or provide an alternative?
Update (solution):
function isConnected(callback) {
setTimeout(function() {
callback(navigator.connection.type !== Connection.NONE);
}, 500);
}
isConnected(function(connected) {
if (!connected)
alert('Not Connected');
});
You simply can't solve your problem that way. Because javascript is single threaded, the setTimeout callback can only run when your JS thread of execution finishes so with your infinite loop, it will never get to run and your code will hang. Eventually the browser will complain about a long-running piece of JS and offer to shut it down.
Instead, you need to use a callback function on the setTimeout() and continue your code from that callback. You can't use sequential code for timeouts. You must use asynchronous coding styles.
You could do something like this where you pass in a callback and it calls the callback back and passes it the connected boolean:
function GetConnectedState(callback) {
setTimeout(function() {
callback(navigator.connection.type !== Connection.NONE);
}, 500);
}
Your existing code doesn't seem to offer anything other than a look at the connection state in 1/2 second. With any real sort of asynchronous operation in javascript (such as an ajax call or a websocket connection), there should also be a success or completion callback that will tell you when/if the operation actually completes and you can also trigger the callback based on that (sooner than 1/2 second most of the time). So, this doesn't really look like a complete solution, but it's all you show us you're using.
For example, here's an answer you can look at that calls a callback when an images loads successfully, with an error or times out with no response: Javascript Image Url Verify and How to implement a "function timeout" in Javascript - not just the 'setTimeout'. A similar concept could be used for implementing your own timeout on some other type of asynchronous call.
As I revisit this 5 years later, can't help but to offer up a fancier alternative:
await new Promise(function(resolve) {
setTimeout(resolve, 500);
});
if (navigator.connection.type === Connection.NONE) {
alert('Not Connected');
}
Javascript is single-threaded. The timeout function can't run until your script returns to the main event loop. But as long as you're in the while loop it will never return, so the timeout function never runs, so done is never set to true.
It's not possible in Javascript to wait for an asynchronous event before returning.
Related
I am new(2 days!!) to the world of JavaScript and my only prior coding experience is in Java where execution of statements takes place sequentially.
I understand that or at least I've read that JavaScript is asynchronous which means that if there is a statement that takes a long time to execute, the next statement is executed without holding up the program for the first statement.
I came across callbacks(a lot actually!!) but I couldn't see how they could be used to determine the order of execution. I wrote a piece of code just to understand how it could be done and I sure could use some help.
console.log("Beginning");
function Test(callback){
setTimeout(function(callback){
console.log("Something that takes a lot of time");
},5000);
callback();
}
function tstCallBack(){
console.log("Should come last");
}
Test(tstCallBack);
What I want is for the output to display -
Beginning
Something that takes a lot of time
Should come last
But the output I am getting is -
Beginning
Should come last
Something that takes a lot of time
Is there anything I can do to get the output in the way I want it?
Let's clear some things up in what you said:
I am new(2 days!!) to the world of JavaScript and my only prior coding
experience is in Java where execution of statements takes place
sequentially. I understand that or at least I've read that JavaScript
is asynchronous which means that if there is a statement that takes a
long time to execute, the next statement is executed without holding
up the program for the first statement.
This is not how it works. A given function is either asynchronous or its synchronous by design. It has absolutely nothing to do with how long it takes to execute. You can have a very quick async function or a very long synchronous function. What determines whether the function is asynchronous or not is how it is designed. If it uses async I/O or timers or any other async infrastructure, then at least some of the execution of the function is asynchronous. That means that some of the function will finish LATER and some of the code right after this function call will execute BEFORE the async portion finishes.
I came across callbacks(a lot actually!!) but I couldn't see how they
could be used to determine the order of execution. I wrote a piece of
code just to understand how it could be done and I sure could use some
help.
Callbacks are used to notify the calling code when some asynchronous operation has completed. This can be used either to consume the result of the asynchronous operation or can be used to execute the next piece of code that wants to run in sequence after the async operation has finished.
In your code example, if you want the desired sequence, then you must call the callback inside the setTimeout() callback so that it gets called AFTER the setTimeout() called executes, thus giving you the desired sequence.
You also have to remove the callback argument to the setTimeout callback. That callback is not passed with that argument so declaring it there is just wrong. It can be accessed directly from the parent function via a closure as shown here:
console.log("Beginning");
function Test(callback){
setTimeout(function(){
console.log("Something that is asynchronous");
// call the callback here to indicate to the calling code
// that the asynchronous operation is now complete
callback();
},5000);
console.log("After Setting Timer");
}
function tstCallBack(){
console.log("Should come last");
}
Test(tstCallBack);
This will generate a sequence in the console of:
Beginning
After Setting Timer
Something that is asynchronous
Should come last
Conceptually, the Javascript engine runs a single thread and that single thread uses an event queue. So, in your function above, this is what happens.
The first console.log("Beginning"); is executed.
Test(tstCallback) is called.
As part of executing the Test() function, a timer is scheduled. This registers a timer internal to the JS engine.
The execution of code in Test() continues, console.log("After Setting Timer"); is executed and then that function finishes.
The current thread of JS execution finishes and if there is nothing else in the event queue, then the JS engine has nothing to do, but wait for the next event to occur.
Some time later (the 5 seconds that your timer is set for), the internal timer fires and it puts the timer event in the JS event queue.
Since there is no other JS executing at the moment, the timer event is pulled out of the event queue and executed. This means that the original callback that was registered for the timer is called.
As the timer callback is called, it executes the console.log("Something that is asynchronous"); line and then calls callback().
Your tstCallback function is then called and console.log("Should come last"); is executed.
The async event finishes execution and the JS engine looks to see if there are any more events in the event queue. If so, the next event is pulled out of the queue and it is run.
There are a number of very good references on how Javascript handles asynchronous operations:
How does JavaScript handle AJAX responses in the background?
How Javascript Timers Work
Do I need to be concerned with race conditions with asynchronous Javascript?
A lot of what you've said is wrong. JavaScript is sequential in the same way as Java, but asynchronous calls are made more often. If you want your callback to be called after the long thing, you must call it after the long running program. Like so -
console.log("Beginning");
function Test(callback){
setTimeout(function(callback){
console.log("Something that takes a lot of time");
callback();
},5000);
}
function tstCallBack(){
console.log("Should come last");
}
Test(tstCallBack);
I have modified your code as below to get the desired output.
console.log("Beginning");
function Test(callback){
console.log("Something that takes a lot of time");
setTimeout(callback,5000);
}
function tstCallBack(){
console.log("Should come last");
}
Test(tstCallBack);
setTimeout takes a callback function that will be executed after the specified time interval
The use of setTimeout is the asynchronous part. When the above code is executed,first the "Begining" console statement is printed and then the Test function is called passing in a function that needs to be executed asynchronously after 500ms .
Place the callback inside setTimeout and not outside as the callback will be executed first before the setTimeout does as javascript won't wait for setTimeout execution(as JS is synchronous by nature) and executes the next line and hence you won't get the desired output.
console.log("Beginning");
function Test(callback){
setTimeout(function(){
console.log("Something that takes a lot of time");
callback();
},5000);
}
function tstCallBack(){
console.log("Should come last");
}
Test(tstCallBack);
Demo
I'm trying to implement this kind of logic in Javascript:
LOOP
doStuff();
END
console.log("Stuff has been done");
I've managed to do it this way:
var loop = function() {
console.log("events");
window.requestAnimationFrame(loop);
}
window.requestAnimationFrame(loop);
console.log("loop is finished");
someOtherCodeGoesHere();
But it doesn't work. Well, it does, but "loop is finished" appears even before RAF is called. This whole code makes sense though, but it's not working as I want it to.
I've also figured out that I can make loop() return a callback function once a condition is met, but I don't want to enclose someOtherCodeGoesHere(); inside it because it's not what I want. Let's say if I have 10 loops, I'd have a callback hell. I just want it to keep going with the code flow, like a plain GOTO if you will.
Any ideas are welcome! :)
I am not familiar with the window.requestAnimationFrame() method, but since you are passing a callback as its only parameter I assume it is an asynchronous function. This means that its invocation does not block the execution of the methods after it. There is no guarantee that the RAF method will call the callback you passed in before the console.log is called. Normally any logic you want to happen after an async method runs you should put in the callback. Dealing with callbacks can be a pain so libraries like async can be a great help. Here is an example of how you might write this using the async lib. (This code is assuming you wanted an infinite loop due to the recursion your attempting in your code)
var loop = function() {
window.requestAnimationFrame(function() {
console.log("events");
});
}
async.forever(loop, function(err) {
console.log(err); // Only gets called if an error occurs
});
var label = prompt('Label for Vertical Line');
This code returns the value in label which I enter in the prompted field. But I want some time delay to get the prompted value.
I'm using this code:
var label=alertWithoutNotice();
function alertWithoutNotice(){
var lab;
setTimeout(function(){
lab=prompt('Label for Vertical Line');
}, 1200);
return lab;
}
But this doesn't return any value. Please tell me what is wrong?
This is a chronological issue. setTimeout schedules an asynchronous event. That means, until it runs, the rest of your script will continue to execute.
The result is your return lab line is executed before the timeout callback fires. And so on the return, lab is undefined at that point.
As such, you can't return values from asynchronous events, or at least not meaningfully.
Something like jQuery's deferred objects would be your best route to a fix here, but I don't know if jQuery is an option for you.
The problem is that you are returning value before get inside timeout because setTimeout function is asynchronous.
Take a look here to use synchronously: using setTimeout synchronously in JavaScript
Since you are dialing with asynchronous operation the simplest approach here is to use a callback function. For example:
alertWithoutNotice(function(label) {
alert(label)
});
function alertWithoutNotice(callback) {
setTimeout(function() {
callback(prompt('Label for Vertical Line'));
}, 1200);
}
So think of it is that alertWithoutNotice is notified when label is available. When it happens callback is invoked.
Knowing that while Node.js is working asynchronously, writing something like this:
function sleep() {
var stop = new Date().getTime();
while(new Date().getTime < stop + 15000) {
;
}
}
sleep();
console.log("done");
...would call the sleep(), block the server for the duration of the while loop (15secs) and just THEN print "done" to the console. As far as I understand, this is because Node.js is giving JavaScript only access to the main thread, and therefore this kidn of thing would halt further execution.
So I understand the solution to this is to use callbacks:
function sleep(callback) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + 15000) {
;
}
callback();
}
sleep(function() {
console.log("done sleeping");
});
console.log("DONE");
So I thought this would print 'DONE' and after 15 secs. 'done sleeping', since the sleep() function gets called and is handed a pointer to a callback function. While this function is working (the while loop), the last line would be executed (print 'done'). After 15 seconds, when the sleep() function finishes, it calls the given callback function, which then prints 'done sleeping'.
Apparently I understood something wrong here, because both of the above ways block. Can anybody clarify please?
Thanks in advance,
Slagjoeyoco
Javascript and node.js are single threaded, which means a simple while blocks; no requests/events can be processed until the while block is done. Callbacks don't magically solve this problem, they just help pass custom code to a function. Instead, iterate using process.nextTick, which will give you esentially the same results but leaves space for requests and events to be processed as well, ie, it doesn't block:
function doSleep(callback) {
var stop = new Date().getTime();
process.nextTick(function() {
if(new Date().getTime() < stop + 15000) {
//Done, run callback
if(typeof callback == "function") {
callback();
}
} else {
//Not done, keep looping
process.nextTick(arguments.callee);
}
});
}
doSleep(function() {
console.log("done sleeping");
console.log("DONE");
});
You are calling sleep right away, and the new sleep function blocks. It keeps iterating until the condition is met. You should use setTimeout() to avoid blocking:
setTimeout(function () {
console.log('done sleeping');
}, 15000);
Callbacks aren't the same thing as asynchronicity, they're just helpful when you want to get a... callback... from an asynchronous operation. In your case, the method still executes synchronously; Node doesn't just magically detect that there's a callback and long-running operation, and make it return ahead of time.
The real solution is to use setTimeout instead of a busy loop on another thread.
As already mentioned, asynchronous execution should be achieved by setTimeout() rather than while, because while will freeze in one "execution frame".
Also it seems you have syntax error in your example.
This one works fine: http://jsfiddle.net/6TP76/
Why there no such function in javascript that sets a timeout for its continuation, saves the necessary state (the scope object and the execution point), terminates the script and gives the control back to the browser? After the timeout expires the browser would load back the execution context and continues the script, and we would have a real non browser blocking sleep functionality that would work even if the JS engine is single threaded.
Why there is still no such functionality in javascript? Why do we have to still slice our code into functions and set the timeouts to the next step to achieve the sleep effect?
I think 'sleep'ing is something you do not want in your browser.
First of all it might be not clear what has to happen and how a browser should behave when you actually sleep.
Is the complete Script runtime sleeping? Normally it should because you only have one thread running your code. So what happens if other events oocur during sleep? they would block, and as soon execution continues all blocked events would fire. That will cause an odd behaviour as you might imagine (for instance mouse click events which are fired some time, maybe seconds, after the actual click). Or these events had to be ignored, which will lead to a loss of information.
What will happen to your browser? Shall it wait for sleep if the user clicks a (e.g. close window) button? I think not, but this might actually call javascript code again (unload) which will not be able to be called since program execution is sleeping.
On a second thought sleep is a sign of poor program design. Actually a program/function/you name it has a certain task, which shall be completed as soon as possible. Sometimes you have to wait for a result (for instance you wait for an XHR to complete) and you want to continue program execution meanwhile. In this case you can and should use asynchronous calls. This results in two advantages:
The speed of all scripts is enhanced (no blocking of other scripts due to sleep)
The code is executed exactly when it should and not before or after a certain event (which might lead to other problems like deadlocks if two functions check for the same condition ...)
... which leads to another problem: Imagine two or more pieces of code would call sleep. They would hinder themselves if they try to sleep at the same, maybe unnecessarily. This would cause a lot of trouble when you like to debug, maybe you even have difficulties in ensuring which function sleeps first, because you might control this behavior somehow.
Well I think that it is one of the good parts of Javascript, that sleep does not exist. However it might be interesting how multithreaded javascripts could perform in a browser ;)
javascript is desgined for single process single thread runtime, and browser also puts UI rendering in this thread, so if you sleep the thread, UI rendering such as gif animation and element's event will also be blocked, the browser will be in "not responding" state.
Maybe a combination of setTimeout and yield would work for your needs?
What's the yield keyword in JavaScript?
You could keep local function scope while letting the browser keep going about its work.
Of course that is only in Mozilla at the moment?
Because "sleep()" in JavaScript would make for a potentially horrible user experience, by freezing the web browser and make it unresponsive.
What you want is a combination of yield and Deferreds (from jquery for example).
It's called sometimes pseudoThreads, Light Threading or Green Threads. And you can do exactly what you want with them in javascript > 1.7 . And here is how:
You'll need first to include this code:
$$ = function (generator) {
var d = $.Deferred();
var iter;
var recall = function() {
try {var def = iter.send.apply(iter, arguments);} catch(e) {
if (e instanceof StopIteration) {d.resolve(); return;}
if (e instanceof ReturnValueException) {
d.resolve(e.retval); return
};
throw e;
};
$.when(def).then(recall); // close the loop !
};
return function(arguments) {
iter = generator.apply(generator, arguments);
var def = iter.next(); // init iterator
$.when(def).then(recall); // loop in all yields
return d.promise(); // return a deferred
}
}
ReturnValueException = function (r) {this.retval = r; return this; };
Return = function (retval) {throw new ReturnValueException(retval);};
And of course call jquery code to get the $ JQuery acces (for Deferreds).
Then you'll be able to define once for all a Sleep function:
function Sleep(time) {
var def = $.Deferred();
setTimeout(function() {def.resolve();}, time);
return def.promise();
}
And use it (along with other function that could take sometime):
// Sample function that take 3 seconds to execute
fakeAjaxCall = $$(function () {
yield (Sleep(3000));
Return("AJAX OK");
});
And there's a fully featured demo function:
function log(msg) {$('<div>'+msg+'</div>').appendTo($("#log")); }
demoFunction = $$(function (arg1, arg2) {
var args = [].splice.call(arguments,0);
log("Launched, arguments: " + args.join(", "));
log("before sleep for 3secs...");
yield (Sleep(3000));
log("after sleep for 3secs.");
log("before call of fake AjaxCall...");
ajaxAnswer = yield (fakeAjaxCall());
log("after call of fake AjaxCall, answer:" + ajaxAnswer);
// You cannot use return, You'll have to use this special return
// function to return a value
log("returning 'OK'.");
Return("OK");
log("should not see this.");
});
As you can see, syntax is a little bit different:
Remember:
any function that should have these features should be wrapped in $$(myFunc)
$$ will catch any yielded value from your function and resume it only when
the yielded value has finished to be calculted. If it's not a defered, it'll work
also.
Use 'Return' to return a value.
This will work only with Javascript 1.7 (which is supported in newer firefox version)
It sounds like what you're looking for here is a way to write asynchronous code in a way that looks synchronous. Well, by using Promises and asynchronous functions in the new ECMAscript 7 standard (an upcoming version of JavaScript), you actually can do that:
// First we define our "sleep" function...
function sleep(milliseconds) {
// Immediately return a promise that resolves after the
// specified number of milliseconds.
return new Promise(function(resolve, _) {
setTimeout(resolve, milliseconds);
});
}
// Now, we can use sleep inside functions declared as asynchronous
// in a way that looks like a synchronous sleep.
async function helloAfter(seconds) {
console.log("Sleeping " + seconds + " seconds.");
await sleep(seconds * 1000); // Note the use of await
console.log("Hello, world!");
}
helloAfter(1);
console.log("Script finished executing.");
Output:
Sleeping 1 seconds.
Script finished executing.
Hello, world!
(Try in Babel)
As you may have noticed from the output, this doesn't work quite the same way that sleep does in most languages. Rather than block execution until the sleep time expires, our sleep function immediately returns a Promise object which resolves after the specified number of seconds.
Our helloAfter function is also declared as async, which causes it to behave similarly. Rather than block until its body finishes executing, helloAfter returns a Promise immediately when it is called. This is why "Script finished executing." gets printed before "Hello, world!".
Declaring helloAfter as async also allows the use of the await syntax inside of it. This is where things get interesting. await sleep(seconds * 1000); causes the helloAfter function to wait for the Promise returned by sleep to be resolved before continuing. This is effectively what you were looking for: a seemingly synchronous sleep within the context of the asynchronous helloAfter function. Once the sleep resolves, helloAfter continues executing, printing "Hello, world!" and then resolving its own Promise.
For more information on async/await, check out the draft of the async functions standard for ES7.