Why is my synchronous code using setTimeout behaving asynchronous in JavaScript? - javascript

I'm trying to loop 10x over a test (waiting for the condition to be true), but somehow it's not working.
Here is what I have:
// my counter
$.testHelper.countDown = function(test, iteration) {
var ticker = iteration || 0;
if (ticker > 10) {
return false;
}
window.setTimeout(function() {
if (test === true) {
console.log("SENDING");
return true;
}
ticker += 1;
$.testHelper.countDown(test, ticker);
}, 1000);
};
// my test
$.testHelper.testForElement = function(element) {
var result;
console.log($i.find(element).length > 0); // true
result = $.testHelper.countDown(
$i.find(element).length > 0
);
console.log(result); // undefined
return result;
};
My problem is that although my condition equates to true before I'm calling countdown, the answer from countDown is undefined. So my logs come in like this:
// true - before firing my countDown method
// undefined - should not log
// SENDING - response from countDown (= true)
Question:
From the code displayed, is there a reason, why my undefined is logged before countDown is through and returns true?
Thanks!

Erm, because setTimeout is always asynchronous? That's kind of the whole point.
Here's a possibility for you:
function when(condition,then) {
// condition must be a callback that returns `true` when the condition is met
if( condition()) then();
else setTimeout(function() {when(condition,then);},1000);
}
This will poll once every second until the condition is met, and then do whatever is given in the then callback.

Related

Swap DOM Elements with Timeout

I currently try to get into JavaScript a bit. (vanilla)
So at the moment what i want to do is swap different element nodes.
As an example imagine a list with different entries, which later shall be swapped.
For this for element.length times random elements should be swapped. After each of this swaps should be a timeout.
The problem which i now encounter is that the list only updates after the shuffle function finishes.
It is intended to swap a pair, wait for 1 second and then do the next swap.
function shuffle(element){
disableButtons();
let clicked_elmnt = document.getElementsByTagName(element);
console.log(clicked_elmnt);
if(clicked_elmnt.length !== 1 && clicked_elmnt !== 0){
for (let i = 0; i<clicked_elmnt.length;++i){
setTimeout(function(){doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length))}, 1000)
}
}
enableButtons();
}
This code should check if there is a list in a list and then roll new numbers if it tries to swap a parent with its child. (Seems to be working)
function doIt(element,first,second){
let clicked_elmnt = document.getElementsByTagName(element);
if(clicked_elmnt[first].children.length > 0){
if(clicked_elmnt[first].firstElementChild.isSameNode(clicked_elmnt[second].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
if(clicked_elmnt[second].children.length > 0){
if(clicked_elmnt[second].firstElementChild.isSameNode(clicked_elmnt[first].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}
}else{
if(clicked_elmnt[second].children.length > 0){
if(clicked_elmnt[second].firstElementChild.isSameNode(clicked_elmnt[first].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}
}
And the swap then happens in the next function. Here they are swapped.
function doSwap(n1, n2){
console.log("swap");
const afterN2 = n2.nextElementSibling;
const parent = n2.parentNode;
if (n1 === afterN2) {
parent.insertBefore(n1, n2);
} else {
n1.replaceWith(n2);
parent.insertBefore(n1, afterN2);
}
}
Hope someone can help me here without that much weird stuff.
Cheers!
My understanding is you wish that the doIt calls, the callbacks of setTimeout to be 1 second apart from each other. The way setTimeout works is the callbacks are queued to be executed x milliseconds after setTimeout is called. If you want one function to be called 1 second after the previous function finishes, you could call another setTimeout from inside the callback.
Here's your example adjusted as such - the doItLater function includes a setTimeout call, and will recursively call itself clicked_elmnt.length times. You may be able to set it up more cleanly for a real scenario, but this fits without deviating too much from your example.
function shuffle(element) {
disableButtons();
let clicked_elmnt = document.getElementsByTagName(element);
if (clicked_elmnt.length !== 1 && clicked_elmnt !== 0) {
const doItLater = function (i) {
setTimeout(() => {
doIt(element, randomNumber(clicked_elmnt.length), randomNumber(clicked_elmnt.length));
if (i + 1 < clicked_elmnt.length) {
doItLater(i + 1);
}
}, 1000);
}
doItLater(0);
}
enableButtons();
}
If you want to only call enableButtons after the last doIt call finishes, you could move that call to the else clause of if (i + 1 < clicked_elmnt.length).

Javascript - If condition is met, return True for specific amount of time, then false

I'm trying to code a function that, if some conditions are met, it returns "true" for some time, then return false again, even if the condition is still true.
The idea of the code is something like this:
if(condition is met)
{return 'True' for 3000ms then 'false'} //even if the condition it's still 'true'
else {False}
I can easily program the result of the condition (returning true or false if met), but i'm having a lot trouble trying to (if true) returning it for a specific amount of time.
NOTE: a function with setTimeout() is not an option in this case.
Thanks!
You can use closure for this scenario
function falseReturnAfter(time) {
let val = true;
setTimeout(() => {
val = false;
}, time);
return function(con) {
if(con && val) {
return true;
}
return false;
}
}
let f = falseReturnAfter(10000) // return false after 10 sec.
f(true) // true
f(false) // false
// after 10 sec
f(true) // false
f(false) // false
Maybe this will help you.

Break setTimeout function inside foreach loop

I am trying to break the setTimeout function which is started on page load. So what I am doing here is, If I click on the button then I making flag value to true and setTimeout should break which isn't happening here.
This setTimeout function is inside the for each loop. Below is my code.
rData[0].dt.forEach(function(d, i) {
setTimeout(function() {
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
}else{
console.log(flag);
clearTimeout();
return;
}
}, i * 2000);
});
Instead of creating all timeouts in one go, only create them when needed. This way you don't have to clear any of them when you have determined to stop:
(function repeat(list, i) {
if (i >= list.length) return; // nothing (more) to do
var d = list[i]; // do you need d at all??
setTimeout(function() {
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
repeat(list, i+1); // schedule next timeout only now.
}else{
console.log(flag);
// Don't schedule next timeout. This breaks the "loop".
}
}, 2000); // trigger 2 seconds from now. Note: no multiplying anymore.
})(rData[0].dt, 0); // pass initial values: the array and index.
In your version of the code, you would have to keep the id values returned by all setTimeout calls, and then pass them all (or at the least the remaining ones) to clearTimeout, one by one. This would make your code quite cumbersome. I think the above is a more efficient approach.
setTimeout cannot be stopped from its callback itself. setTimeout
returns a timeoutId which can be passed to clearTimeout which in turn will
stop that particualr timer.
One way to stop all such timers is to create an array of timeoutIds and make changes as following.
var timerIds = [];
rData[0].dt.forEach(function(d, i) {
timerIds.push(setTimeout(function(){
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
}
else{
console.log(flag);
}
}, i * 2000));
});
function stopTimeouts(){
timerIds.forEach(function(id){
clearTimeout(id);
}
}
function codeThatMightChangeFlag(callback) {
// do a bunch of stuff
if (condition happens to change flag value) {
// call the callback to notify other code
stopTimeouts();
}
}
Refer: Clear array of setTimeout's and Javascript - wait until flag=true

Wait for Function to complete and then return a value

I have this function Offline.check(); , which takes 1 seconds to execute..So below function is not waiting for it and it always return false on first time.I used set time out..but thats always returning null.
function checkstats()
{
Offline.check(); // This returns Offline.state=up or down and it takes 1 seconds to complete.
if(Offline.state=="up")
{
return true;
}
else
{
return false;
}
}
var a = checkstats();
Ideally you could set a callback function with Offline.check, but I understand it is external, so that won't work.
You can use a timeout to wait for Offline.state to get set, but then you'll need to do any actions involving the variable a asynchronously too:
function checkstats(callBack){ // checkstats() now takes a callback
Offline.check(); // Start Offline.check() as usual
setTimeout(function(){ // Set a timeout for 1 second
if(Offline.state=="up") // After 1 second, check Offline.state as usual
{
callBack(true); // ...but we call the callback instead of returning
}
else
{
callBack(false); // ...but we call the callback instead of returning
}
}, 1000);
}
checkstats(function(a){ // This anonymous function is the callback we're using
// Now you can use "a" normally
});
If you're not sure that Offline.check() will take exactly 1 second, you can use an interval instead of a timeout, and try every second for, say, 5 seconds:
function checkstats(callBack){
Offline.check();
var attempt = 0, maxAttempts = 5;
var checkStatsInterval = setInterval(function(){
if(++attempt > maxAttempts){
// Ran out of attempts, just give up
clearInterval(checkStatsInterval);
alert('Waited '+maxAttempts+' seconds for Offline data. Giving up!');
return;
}
if(Offline.state){
clearInterval(checkStatsInterval);
// It's loaded! Now confidently check Offline.state
if(Offline.state=="up")
{
callBack(true);
}
else
{
callBack(false);
}
}
}, 1000);
}
checkstats(function(a){
// Now you can use "a" normally
});
You can use Asynchronous JavaScript to address the issue. There are several ways of implementing asynchronous behaviour in JavaScript. You can use Callbacks, Listeners or Promises.
Anyway, if you are certain that it only takes 1 second, setTimeout in a callback function and allow Offline.check() to complete. (If it's external or lazy to implement async there)
doOfflineCheck(function(){
if(Offline.state=="up")
{
return true;
}
else
{
return false;
}
});
function doOfflineCheck(cb){
setTimeout(function(){
Offline.check();
},1000);
}

Blocking "wait" function in javascript?

As part of a Javascript project I'm working on, there are some synchronous ajax calls (I guess that makes it "sjax", but I digress). I'm now writing a debugging panel which would allow me to test out the site with some artificially simulated network conditions by wrapping $.ajax. Simple things: faking a 500 response etc, and making the ajax calls take much longer.
For the asynchronous calls, it's simple. When the real response comes back, add a setTimeout to make it wait for the artificial response time before triggering the callback. However, this doesn't work with the synchronous calls obviously, since setTimeout isn't synchronous.
So, is there a way to make a Javascript program perform a blocking wait for a set amount of time?
The only thing I could think of would be something like this:
function wait(ms) {
var start = +(new Date());
while (new Date() - start < ms);
}
Is there a better solution?
(Also, please assume there's a good reason for the blocking ajax calls... :-\)
Do not do it on the JavaScript level. Get a proxy such as Fiddler and set up an AutoResponder to delay the call by a time period.
If it's just for debugging purposes to have an artificial delay:
alert('block me one more time');
There is no reasonable other approach to have a blocking code in ECMAscript. Since Javascript is executed in the same thread ("UI thread") which browsers use to render the DOM and to certain other things, the whole show was designed not to block anything.
Of course you can fake it by using a loop, but its a perversion of the show.
I figured this code might help
// execute code consecutively with delays (blocking/non-blocking internally)
function timed_functions()
{
this.myfuncs = [];
this.myfuncs_delays = []; // mirrors keys of myfuncs -- values stored are custom delays, or -1 for use default
this.myfuncs_count = 0; // increment by 1 whenever we add a function
this.myfuncs_prev = -1; // previous index in array
this.myfuncs_cur = 0; // current index in array
this.myfuncs_next = 0; // next index in array
this.delay_cur = 0; // current delay in ms
this.delay_default = 0; // default delay in ms
this.loop = false; // will this object continue to execute when at end of myfuncs array?
this.finished = false; // are we there yet?
this.blocking = true; // wait till code completes before firing timer?
this.destroy = false; // <advanced> destroy self when finished
// handle next cycle execution
this.next_cycle = function() {
var that = this;
var mytimer = this.delay_default;
if(this.myfuncs_cur > -1)
if(this.myfuncs_delays[this.myfuncs_cur] > -1)
mytimer = this.myfuncs_delays[this.myfuncs_cur];
console.log("fnc:" + this.myfuncs_cur);
console.log("timer:" + mytimer);
console.log("custom delay:" + this.myfuncs_delays[this.myfuncs_cur]);
setTimeout(function() {
// times up! next cycle...
that.cycle();
}, mytimer);
}
this.cycle = function() {
// now check how far we are along our queue.. is this the last function?
if(this.myfuncs_next + 1 > this.myfuncs_count)
{
if(this.loop)
{
console.log('looping..');
this.myfuncs_next = 0;
}
else
this.finished = true;
}
// first check if object isn't finished
if(this.finished)
return false;
// HANDLE NON BLOCKING //
if(this.blocking != true) // blocking disabled
{
console.log("NOT BLOCKING");
this.next_cycle();
}
// set prev = current, and current to next, and next to new next
this.myfuncs_prev = this.myfuncs_cur;
this.myfuncs_cur = this.myfuncs_next;
this.myfuncs_next++;
// execute current slot
this.myfuncs[this.myfuncs_cur]();
// HANDLE BLOCKING
if(this.blocking == true) // blocking enabled
{
console.log("BLOCKING");
this.next_cycle();
}
return true;
}; // END :: this.cycle
// adders
this.add = {
that:this,
fnc: function(aFunction) {
// add to the function array
var cur_key = this.that.myfuncs_count++;
this.that.myfuncs[cur_key] = aFunction;
// add to the delay reference array
this.that.myfuncs_delays[cur_key] = -1;
}
}; // end::this.add
// setters
this.set = {
that:this,
delay: function(ms) {
var cur_key = this.that.myfuncs_count - 1;
// this will handle the custom delay array this.that.myfunc_delays
// add a custom delay to your function container
console.log("setting custom delay. key: "+ cur_key + " msecs: " + ms);
if(cur_key > -1)
{
this.that.myfuncs_delays[cur_key] = ms;
}
// so now we create an entry on the delay variable
}, // end :: this.set.delay(ms)
delay_cur: function(ms) { this.that.delay_cur = ms; },
delay_default: function(ms) { this.that.delay_default = ms; },
loop_on: function() { this.that.loop = true; },
loop_off: function() { this.that.loop = false; },
blocking_on: function() { this.that.blocking = true; },
blocking_off: function() { this.that.blocking = false; },
finished: function(aBool) { this.that.finished = true; }
}; // end::this.set
// getters
this.get = {
that:this,
delay_default: function() { return this.that.delay_default; },
delay_cur: function() { return this.that.delay_cur; }
}; // end::this.get
} // end ::: timed_functions()
And Test...
// // // BEGIN :: TEST // // //
// initialize
var fncTimer = new timed_functions;
// set some defaults
fncTimer.set.delay_default(1000); // set a default delay between function blocks
fncTimer.set.blocking_on(); // next timer begins count before code is executed
fncTimer.set.blocking_off(); // next timer begins count after code is executed
// fncTimer.set.loop_on(); // when finished start over
// fncTimer.set.loop_off();
// BEGIN :: ADD FUNCTIONS (they will fire off in order)
fncTimer.add.fnc(function() {
console.log('plan a (2 secs)');
});
fncTimer.set.delay(2000); // set custom delay for previously added function
fncTimer.add.fnc(function() {
console.log('hello world (delay 3 seconds)');
});
fncTimer.set.delay(3000);
fncTimer.add.fnc(function() {
console.log('wait 4 seconds...');
});
fncTimer.set.delay(4000);
// END :: ADD FUNCTIONS
// NOW RUN
fncTimer.cycle(); // begin execution
// // // END :: TEST // // //

Categories

Resources