I am having trouble with Javascript async call. The Car class below has a move function which takes two arguments, the first one is a value, the second one is a call back function, which will be called after 1 second, and this callback function takes the value returned by forward method.
var Car = function() {
this._count = 0;
};
Car.prototype = {
move: function(value, onMoved) {
setTimeout(function() {
onMoved(this.forward(value));
}.bind(this), 1000);
},
forward: function(value) {
this._count = this._count + value;
return this._count;
}
};
I want to call the move function like this:
var values = [1, 2, 3, 4];
var car = new Car();
values.forEach(function(value) {
car.move(value, function(result) {
console.log(result);
});
});
Now my problem is the call back function onMoved does not wait for 1 second to execute between each value that it is outputting. How can I make it so it wait between each value that it is outputting? I am allowed to use underscore.js. Thanks.
setTimeout in javascript registers callback function in a queue to execute in future once the current execution stack is free. for example:-
while(1){
setTimeout(function(){
console.log('hello');
},1000);
}
this will not print hello as the execution stack will never be free.
Coming back to the example we call move method which will be pushed to a queue. After one second it starts executing each function one by one without any delay as the setTimeout is set to a fixed time ie. 1000 millisecond.
workaround:-
var Car = function() {
this._count = 0;
};
Car.statCount = 0;
Car.prototype = {
move: function(value, onMoved) {
this.constructor.statCount++;
setTimeout(function() {
onMoved(this.forward(value));
}.bind(this), 1000*Car.statCount);
},
forward: function(value) {
this._count = this._count + value;
return this._count;
},
constructor: Car
};
var values = [1, 2, 3, 4];
var car = new Car();
values.forEach(function(value) {
car.move(value, function(result) {
console.log(result);
});
});
Since you want a 1 sec. wait between each call, you might consider using the setInterval method on window, instead of utilizing the setTimeout method:
You could add some method performing some actions for each iteration, and finish iterating when all values are processed:
var i=0;
function go(result) {
if (!!values[i]) {
car.move(values[i], function(result) {console.log(result);})
i++;
} else {
window.clearInterval(interval);
}
}
And call this function using setInterval
var interval = window.setInterval(go, 1000);
Have a look at the following fiddle:
https://jsfiddle.net/adw1k98m/1/
(see console log for output)
Related
I try to write the function that returns all results of asynchronous functions and execute a callback that push into an array and log the result of every async function.
As a waiter that brings all dishes when they are all done.
I don't understand how to get the child arguments that should be returned as a result. The code of task and my not working solution is below:
The task:
var dishOne = function(child) {
setTimeout(function() {
child('soup');
}, 1000);
};
var dishTwo = function(child) {
setTimeout(function() {
child('dessert');
}, 1500);
};
waiter([dishOne, dishTwo], function(results) {
console.log(results); // console output = ['soup', 'dessert']
});
My not working solution:
function child(arg) {
this.arr.push(arg)
}
function waiter(funcArray, doneAll) {
var result = {
arr: []
};
let i = 0;
const x = child.bind(result)
funcArray.forEach(function(f) {
f(x)
i++;
if(i == 2) {
doneAll(result.arr)
}
});
}
Problem is this part:
funcArray.forEach(function(f) {
f(x)
i++;
if(i == 2) {
doneAll(result.arr)
}
});
which is a synchronous function so when you check if(i == 2), you basically check, that you have called all async functions, but they did not returned anything yet, so all you know is, that the functions have been called, but result.arr is not yet populated.
You must move the doneAll(result.arr) expression into child callback, then it will be called by async function as it returns result.
Simpliest solution I can think of is writing your child as
function child(arg) {
if (this.arr.push(arg) === this.allCount) this.doneAll(this.arr);
}
and in your waiter function enhance result object
var result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
This shall work, but has one drawback -- position of results does not keep position of functions in funcArray, the position of results is sorted by duration of async function, simply the first resolved would take first result etc. If this is a problem, you must pass also index to your child function to store result at precious position in result array and then the check by arr.length would not work, because JS array returns length as the highest index + 1, so if your last funcArray would fulfill first, it'll fill last index and the length of result.arr will be equal to this.allCount, so for keeping order of result the same as funcArray, you will need to store number of returned results as another number, increase that number with every new result and compare that number to allCount.
Or decrease allCount like so
function child(idx, arg) {
this.arr[idx] = arg;
if (--this.allCount === 0) this.doneAll(this.arr);
}
And modify your waiter function
function waiter(funcArray, doneAll) {
const result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
funcArray.forEach(function(f, i) {
f(child.bind(result, i));
});
}
Why not Promise?
function dishOne() {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve('soup') }, 1000)
})
}
function dishTwo() {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve('dessert') }, 1500)
})
}
Your waiter function:
function waiter(dishes, callback) {
return Promise.all(dishes).then(callback)
}
And you can use it like this
waiter([dishOne(), dishTwo()], function(results) {
// Invoked when all dishes are done
console.log(results) // ['soup', dessert']
})
Much easier to understand. Right?
Say i have a function like :
var bigArray = [1,2,3,4,5.........n];
for(var i=0; i<bigArray.length; i++){
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
.
.
.
.
.
.
.
.
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
}
Now what i want from this function is that when all of the conditions have performed their intended task, only then it should move to the next iteration of FOR loop. In other words, i=0 in FOR LOOP should change to i=1 and so-on iff all of the conditions inside the FOR loop have completed their job in current iteration.
Currently behaviour of this code is very random (because of setTimeout i believe). How can i make this work as per my expectations?
I recently read about promises (don't know much about them) but i'm not sure how to implement them in this code or if they will work in this case...
The original idea came from the answer of Nina Scholz on some similar question.
this answer is good if you don't like promises and deferred object. Othewise Kris Kowal's Q library would be a better choice.
function Iterator() {
var iterator = this;
this.queue = [];
this.add = function(callback, wait) {
iterator.queue.push(iterator.wait(wait));
iterator.queue.push(function() {
callback();
iterator.next();
});
};
this.wait = function(wait) {
return function() {
setTimeout(iterator.next, wait);
};
};
this.next = function() {
iterator.queue.length && iterator.queue.shift()();
};
}
var arr = [1, 2, 3, 4, 5],
counter = -1;
var iterator = new Iterator();
(function fillNextIteration() {
if (counter >= arr.length)
return;
counter++;
iterator.add(function() {
console.log('1 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('2 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('3 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('4 - counter value is ' + counter);
}, 100);
iterator.add(fillNextIteration, 100);
iterator.next();
})();
Code Explanation
Iterator class has one queue which is and array and some methods:
next
when you call next() if there is any callback in queue it will fetch first and execute that. when you call Array#shift it removes and returns first item from array. When this item is a function then you can invoke it by adding parentheses in front of it. here is the expanded version:
if (iterator.queue.length > 0) {
var callback = iterator.queue.shift();
callback();
}
wait
This method will add an extra callback to queue which after a timeout calls the next method. this one is for creating the expected delay.
add
This method calls the wait with desired delay then attaches another function to queue which will call the callback and then calls next to make callback chain running.
fillNextIteration
after explaining the iterator there is another loop here on function fillNextIteration this starts with the conditions for the first iteration for example:
if (someConditionIsTrue) {
iterator.add(function() {
doWhatShallBeDone;
}, delay);
}
you can check all conditions and then if required add the callback as shown.
after all condition finished add fillNextIteration as last action to continue the loop.
self invoking function
As you can see fillNextIteration is self invoked.
This helps to reduce one invoke and works like this:
function f(){};
f();
Last thing to mention
you can create a loop by calling a function inside itself. if there is no delay or stop for revoke then it would be a deadlock. So this is another loop as well:
(function loop() { setTimeout(loop, delay); })();
Try this please :
bigArray = [1, 2, 3, 4, 5.........n];
neededStuff = 10; // Number of finished works you need below
stuffOK = neededStuff;
function mainFunction(i) {
function loopFunction(i) {
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
/*
...
*/
if(someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
}
if (stuffOK == neededStuff) {
i++;
stuffOK = 0; // reinit counter
loopFunction(i);
}
setTimeout('mainFunction(' + i + ');', 100);
}
mainFunction(-1);
Hope this will work from promise chaning
callSomeFunction1().then(function() {
callSomeFunction2();
}).then(function() {
callSomeFunction3();
})
I am trying to iterate through a list of orders, pausing each one for 3 seconds. Then once complete, start again in a loop.
In the snippet below, if the loop is enabled it seems to be starting the loop again before the first loop has completed.
Also the 3 second pause between each call to display_notification is not working.
start: function() {
$.each(this.orders, function(index, order) {
setTimeout(function() {
console.log ('show notification');
recentlyApp.display_notification(order);
}, index * 3000);
});
if (this.settings.loop) {
this.start();
}
},
display_notification: function(order) {
console.log(order);
}
As soon as you're already using jQuery, you can create a plugin to loop through an array in an interval.
Example:
// timer function that loops through an array with in a given interval
$.timer = function (list, callback, time/*, onFinish, index*/) {
var onFinish = arguments.length > 3 ? arguments[3] : void 0,
index = arguments.length > 4 ? arguments[4] : 0;
if (index < list.length) {
list.__timed = setTimeout(function() {
callback.call(this, index, list[index]);
$.timer(list, callback, time, onFinish, ++index);
}, time);
}
else if (onFinish){
onFinish.call(this);
}
return {
cancel: function() {
if (list.__timed !== void 0) {
clearTimeout(list.__timed);
delete list.__timed;
}
}
};
}
// usage
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var timer = $.timer(arr, function (index, item) {
document.querySelector('div').insertAdjacentHTML('beforeend', '<div>' + item + '</div>');
}, 3000, function() {
document.querySelector('div').insertAdjacentHTML('beforeend', '<div>Finished</div>');
});
// cancelling the loop
$('button').click(function(e) {
timer.cancel();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
<button>Cancel timer</button>
So, in your scenario, it would be something like this:
start: function() {
$.timer(this.orders, function(index, order) {
console.log('show notification');
recentlyApp.display_notification(order);
}, 3000, this.settings.loop ? this.start : void 0);
},
display_notification: function(order) {
console.log(order);
}
UPDATE
Now, the $.timer is returning an object with the cancel method, so you can cancel the loop at anytime by calling .cancel();.
I've created a __timed property into the array to always get the last setTimeout executed, so when you call the cancel method, it calls the clearTimeout for what it was going to call next.
You're calling this.start() as soon as you've finished the loop that sets up the timeout functions.
Either track the last value of index and use it to set a timeout on the call to start() or put the call to start() in the last timed out function you set going in the loop.
I'm reading a tutorial for Nodejs, but I can't understand this snippet of code, please explain it to me.
function async(arg, callback) {
console.log('do something with \''+arg+'\', return 1 sec later');
setTimeout(function() { callback(arg * 2); }, 1000);
}
function final() { console.log('Done', results); }
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;
function launcher() {
while(running < limit && items.length > 0) {
var item = items.shift();
async(item, function(result) {
results.push(result);
running--;
if(items.length > 0) {
launcher();
} else if(running == 0) {
final();
}
});
running++;
}
}
launcher();
This code produces run 2x then pause for a second then run 2x again until there is no item in the items array.
But when I removed the anonymous function in the setTimeout:
setTimeout(callback(arg*2), 1000);
Then the code runs with out stopping any second. Why?
Then the code runs with out stopping any second. Why?
Because instead of passing a function to setTimeout, you are passing the return value of a function call to it.
The function call executes immediately, and then setTimeout does nothing with the return value because it (presumably) isn't a string or a function.
Don't remove the anonymous function wrapper.
The reason the anonymous delegate is needed is because setTimeout expects an object of type function as it's first argument.
So you can either give it just a pre-defined function name directly:
function foo()
{
//do something
}
setTimeout(foo, 1000);
Or a delegate:
setTimeout(function(){/*dosomething*/}, 1000);
But this:
setTimeout(foo(param), 1000);
Is invalid because foo(param) isn't semantically correct in this context.
The alternative is to do something like:
setTimeout("foo(param);", 1000);
Because setTimeout will also accept a string of a function, but this is a bad idea because you lose the current scope and it's a pain to debug.
I have a JQuery's .each loop that calls a function with a parameter per iteration, is there a way to delay this function call? I have tried setTimeout as in the following but this does not work as the function gets executed immediately.
$.each(myArray, function (j, dataitem)
{
setTimeout(function () { showDetails(dataitem) }, 300);
});
function showDetails(dataitem)
{
...
}
Array size is roughly 20, What I'm trying to do is to distribute function calls over a certain time frame instead of immediately, any idea how to achieve this? I'm prepared to rewrite and restructure how functions get called to get this done, any help would be appreciated.
You could use the index of the array to calculate the interval dynamically:
$.each(myArray, function (j, dataitem) {
window.setTimeout(function () {
showDetails(dataitem)
}, (j + 1) * 300);
});
You execute them all after 300 milliseconds. Instead, try something like this:
window.setTimeout(function () { showDetails(dataitem) }, (j + 1) * 300);
Edit: instead of creating 20 timers at once I think it's better to do it one by one. Function should be:
function showDetails(index)
{
if (index >= myArray.length)
return false;
var dataItem = myArray[index];
//code here......
//code here......
//code here......
windows.setTimeout(function() { showDetails(index + 1); }, 300);
}
And first call can be:
$(document).ready(function() {
{
showDetails(0);
});
This assume myArray is plain global array, and will handle one item and only then call the next item with delay.
Take a look at jQuery.queue([ queueName ], callback( next )). This allows you to queue functions up to be called and is what jQuery's animation effects use internally.
It sounds like you would like to implement a queue, although it is not entirely clear you intentions for doing so.
EDIT: re-reading your question, I think other answers better match what you are after, however I thought that I would show you an example of how to achieve delayed function execution with a custom queue.
An example of how you could use a queue.
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
output = $('#output');
// set the queue up
$.each(myArray, function (j, dataitem) {
output.queue('queue', function(next) {
var that = this;
showDetails(dataitem);
window.setTimeout(next,300);
});
});
// start the queue running.
output.dequeue('queue');
function showDetails(dataitem) {
output.append('<div>' + dataitem + '</div>');
}
Just don't use $.each, but something like:
var data = [1, 2, 3, 4, 5];
function showDetails(values, delay) {
console.log(values.shift()); //show the value
if (values.length) {
setTimeout(function() {showDetails(values, delay); }, delay); //schedule next elem
}
}
showDetails(data.slice(0), 300); //dont forget the slice, call-by-reference