assigning an asynchronous function and its callback function to a single variable - javascript

After coming up with the function below, my next assignment is to make it loop every 3 seconds.
Chat.fetch(function(x) // x is an array delivered by .fetch
{
for (var i = 0; i<x.length; i++)
{
Chat.display(x[i]); // and .display only takes strings as an argument
}
});
Now my first beginner's thought is to somehow get all the above into a single variable.
var myvariable = Chat.fetch(etc...
Then I would do something like
setInterval(myvariable, 3000);
Obviously that didn't work. What is it about that function that is stopping this approach?

I would propose a different solution.
// define the callback function
var displayMessages = function(x) // x is an array delivered by .fetch
{
for (var i = 0; i<x.length; i++)
{
Chat.display(x[i]); // and .display only takes strings as an argument
}
// Ivoke Chat.fetch with this callback again after 3 seconds
setTimeout( function(){ Chat.fetch( displayMessages ); }, 3000 );
}
// start polling
Chat.fetch( displayMessages );
Now every time the callback is called it will shcedule Chat.fetch to fire again after 3 seconds after the previous messages have been rendered.

Related

For loop does not allow setTimeout to execute

I have the below function that logs a character in a sentence passed to a function at a certain given time, have a look at the function below:
function print_string(param , timer) {
var strt_point = 0,
str_len = param.length,
self = this;
//typewritter();
typeit();
function typeit() {
setTimeout(function(){
console.log(param.substring(strt_point).charAt(0));
if(strt_point < str_len ) {
strt_point++;
typeit()
}
} , timer);
}
}
var str = print_string("hey there , whats up , hows the weather in Manhatten" , 50);
console.log(str);
The above programmer works fine, now if i add a for loop to the above programme I.E. WRAP THE setTimeout in a for loop ,
function print_string(param , timer) {
var strt_point = 0,
str_len = param.length,
self = this;
for(var i = 0 ; i < str_len ; i++) {
//typewritter();
typeit();
function typeit() {
setTimeout(function(){
console.log(param.substring(strt_point).charAt(0));
if(strt_point < str_len ) {
strt_point++;
typeit()
}
} , timer);
}
}
var str = print_string("hey there , whats up , hows the weather in Manhatten" , 50);
console.log(str);
All the characters are printed at once , Why ?
Why is it that the for loop does not honor the setTimeout interval ? can anybody explain ?
If you want the timer argument to act as an actual interval, use this:
function print_string(param, timer) {
for(var i = 0 ; i < param.length ; i++) {
setTimeout((function(char){
return function () {
console.log(char);
}
})(param.charAt(i)), timer * i);
}
}
var str = print_string("hey there , whats up , hows the weather in Manhatten" , 500);
Here is a fiddle.
The confusion for you is that a for loop happens immediately, or as fast as the processor will allow it. When the setTimeout handler executes, it has a different scope to what you're probably expecting. The scope is no longer within the for loop (because that happened in a different tick of the processor) so the print variable is lost. In order to get around this, I'm using what is called a closure. I'm building a function on the fly, to print the specific variable that I need, and passing it as an argument to setTimeout.
Read more about closures here.
The difference of the two pieces of code is:
In the first one you set the timer each time the timeout function is triggered.
In the second case you set all the timers simultaneously (almost) in each "foreach" iteration, so the events fire the same time.
You can prevent it by timer+=50.
I'll try to exlain:
You are passing a string with 30 characters to the function print_string so that the for loop will iterate and call setTimeout 30 times. After 50ms the first setTimeout callback will be called and will output the first char of your string. The variable strt_point will be incremented. Then the second setTimeout callback of your second for loop iteration will be called immediately and because strt_point is has already been incremented, the second char will be printed.
The problem is that you have ONE variable strt_point for all iterations of the for loop so that all chars are printed after 50ms.
I think you want something like this:
var print_string = function ( param, interval ) {
var
i = 0,
len = param.length,
result = document.getElementById( 'result' ),
// function called for every characters
next = function () {
// get the current character
var char = param.charAt(i);
// do what you want with them
result.innerHTML += char;
// if not the end of the string
if ( ++i < len ) {
// continue after the interval
setTimeout( next, interval );
}
}
next();
}
print_string( 'hey there , whats up , hows the weather in Manhatten!', 50 );
<div id="result"></div>

Have a JS function call destroyed after one time use

I'm working on quite a unique project and have a variable that calls my function, is it possible to have it where after it makes 1 function call, it stops working and becomes useless.
var limiter = function (limit, cb) {
var counter = limit;
return function () {
if (counter > 0) {
cb(counter);
counter--;
}
};
};
var counting = limiter(3, function (data) {
console.log('This function can be used ' + data + ' more times.');
});
for (var i = 0; i < 5; i++) {
counting();
};
Calling limiter with the first paramater as 1 and the second parameter as the function definition will allow you to run the function only one time. In this example, I have created the function 'counting' that will log how many more calls it has until it is useless (It only run three times, despite the for loop calling it five times). The for loop at the bottom just shows that it works. You can also create multiple functions using limiter, without the counters overlapping, as they will each have their own unique scope.
Fiddle: http://jsfiddle.net/Kajdav/acLxxywt/

Executing a procedure only at the last-returning callback

I have this piece of code:
for(var i = 0; i < some_array.length; i++){
some_array[i].asynchronous_function(some, parameter, callback(){
some_procedure();
});
}
I call asynchronous_function for each element of the array, and once the function executed, it fires a callback. I have some procedure in the callback that I would like to execute only if this callback is the last one returning of all the asynchronous_functions called. Is there a way to achieve this without polluting too much the code?
Thanks
count the number of times asynchronous_function is called. when it has been called some_array.length times, you can call some_procedure(). something like this
var numTimesCalled = 0;
for(var i = 0; i < some_array.length; i++){
some_array[i].asynchronous_function(some, parameter, function(){
numTimesCalled ++;
if (numTimesCalled === some_array.length) {
some_procedure()
}
});
}
This should do the job :
// callAll : calls methodName method of all array items.
// uses the provided arguments for the call and adds the callback
// methodName is async and must accept a callback as last argument
// lastCallBack will get called once after all methods ended.
//
function callAll(anArray, methodName, lastCallBack ) {
// create closure to keep count of calls
var callCount = anArrray.length;
// build function that calls lastCallBack on last call
var callIfLast = function() { callCount--; if (!callCount) lastCallBack(); };
// build function arguments
var args = arguments.slice(3).push(callIfLast);
// call all functions
anArray.forEach( function(item) { item[methodName].apply(item, args ) } );
}
callAll(myArray, 'meth', myCallback, 1, 2, 3);
// ...
// --> will call myArray[i].meth(1, 2, 3, callIfLast) for all i.
// and call myCallBack when all calls finished.

calling a javascript function in another javascript function several times

I had a javascript function and it works well:
function playMp3(str) {
...
}
the other script:
function playMp3wholepage() {
var dgs=new Array();
dgs[0] = "/abc.mp3";
dgs[1] = "/dac.mp3";
dgs[2] = "/hf.mp3";
....
dgs[28] = "/er23.mp3";
dgs[29] = "/read/34_15.mp3";
for (i=0;i<=29;i++){
ses = dgs[i]
setTimeout("playMp3(ses);", 2000)
}
}
I want to play all of the sounds in the order that given in second script. but I could not get the second script run, it just play the last sound (dgs[29]), not all of them.
Thank you
When setTimeout callback execute, sec is already dgs[29], you need to create another function scope to perserve the value.
for (var i=0; i<=dgs.length; i++){
(function (i) {
setTimeout(function() {
playMp3(dgs[i]);
}, 2000 * i);
}(i));
}
This is something about closure. You can learn more here.
function playMp3wholepage() {
var dgs=new Array();
dgs[0] = "/abc.mp3";
dgs[1] = "/dac.mp3";
dgs[2] = "/hf.mp3";
for (i=0;i<dgs.length;i++){
ses = dgs[i];
(function(mp3) {
setTimeout(function() {
playMp3(mp3);
}, 2000);
})(ses);
}
}
I'm guessing what's happening is that your loop completes so fast, that all calls to playMp3 are being called essentially at once. However, the last call to setTimeout would have set the last sound to play, and that's why it's all you hear.
Furthermore, use the length or your array, and make your code flexible:
for(var i=0; i<dgs.length; i++) {
}

Why is this javascript not running as expected?

function animateGraph() {
var graph;
for(i=0; i<10; i++)
{
var start = new Date();
while((new Date()) - start <= 500) {/*wait*/}
document.getElementById("timeMark").innerHTML = phoneX[i].epoch;
}
}
The loop works. The wait works. But the document.getElement is not showing up until the last item in the array...why?
Using setTimeout will allow the code to run and not lock up the page. This will allow it to run the code and will not effect other elements on the page.
var cnt = 0;
(function animateGraph() {
document.getElementById("timeMark").innerHTML = phoneX[cnt].epoch;
cnt++;
if (cnt<10){
window.setTimeout(animateGraph,500);
}
})();
The while loop, waiting for a datetime, is not a good way to wait - it just blocks execution. It keeps the browser (including UI, and its updating) frozen until the script finishes. After that, the window is repainted according to the DOM.
Use window.setTimeout() instead:
function animateGraph(phoneX) {
var el = document.getElementById("timeMark")
var i = 0;
(function nextStep() {
if (i < phoneX.length )
el.innerHTML = phoneX[i].epoch;
i++;
if (i < phoneX.length )
window.setTimeout(nextStep, 500);
})();
}
Please note that this runs asynchronous, i.e. the function animateGraph will return before all phoneXes are shown.
Use setTimeout instead of a while loop.
https://developer.mozilla.org/en/DOM/window.setTimeout
Also try something like this.
Javascript setTimeout function
The following snippet uses a helper function to create the timers. This helper function accepts a loop counter argument i and calls itself at the end of the timer handler for the next iteration.
function animateGraph() {
var graph;
setTimeMarkDelayed(0);
function setTimeMarkDelayed(i) {
setTimeout(function() {
document.getElementById("timeMark").innerHTML = phoneX[i].epoch;
if (i < 10) {
setTimeMarkDelayed(++i);
}
}, 3000);
}
}
You actually need some sort of helper function, otherwise you'll end up overwriting the value of i in your for loop in every iteration and by the time your timers run out, i will already be 9 and all handlers will act on the last element in phoneX. By passing i as an argument to the helper function, the value is stored in the local scope of that function and won't get overwritten.
Or you could use setInterval like Radu suggested, both approaches will work.

Categories