calling a javascript function in another javascript function several times - javascript

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++) {
}

Related

Placing timer ID into its function

I would like to put timer ID, returned by setInterval(), into its function:
delay_timeout = setInterval(function () {
test_delay(data['time'], delay_timeout);
}, 1000);
Is it possible? To my mind delay_timeout doesn't have a value at this point...
I don't want to save delay_timeout globally for using later in timer's function to stop it. Several timers may work at the same time.
UPDATE:
Code is not global, it is located here:
socket.on('test_delay', function (data) {
...
});
The point is not to make delay_timeout global and be able to kill timer by some condition within its callback function.
your code works fine if you put the setTimeout call in it's own function like this:
function setTimer(){
var timeId = setTimeout(function(){ console.log(timeId); }, 1);
}
for(var i = 0; i < 10; i ++){
setTimer();
}
here's a fiddle
The delay_timeout variable is available to your callback as it is in the same enclosure.
So long as you are not in the same context and rerun your setTimeout before it has triggered the callback you will be fine. so this won't work:
var timeId;
for(var i = 0; i < 10; i ++){
timeId = setTimeout(function(){ console.log(timeId); }, 1);
}
(see the second half of the fiddle...)

Why setInterval wont stop?

Why console.log(1) gets executed here forever:
var interval = setInterval(function() {
if (true) {
clearInterval(interval);
console.log(1);
}
}, 100);
It depends on the scope within which you're executing this code.
If interval is unique within its scope — be it global or function scope — then this will work as expected.
If, however, you execute this code within a loop (for example), then you are overwriting interval with some new interval on each iteration, breaking your clearInterval call for all but the very last setInterval call:
for (var i = 0; i < 3; i++) {
var interval = setInterval(function() {
if (true) {
clearInterval(interval);
console.log(1);
}
}, 100);
}
// ^ will give you one single console log entry,
// and two more console log entries per second forever
It's seems that your variable interval is used somewhere again. If I run code you provided it works as expected. I guess user Lightness has given a great explaination of this, also he provided piece of code where "closure problem" is obvious (which caused you problem too). I just want to add extra information. If you want your code inside of loop + setInteval works aparat you can do the following:
for (var i = 0; i < 3; i++) {
var o = {
i: i,
interval: null,
timer: function() {
if (true) {
clearInterval(this.interval);
console.log(this.i);
}
}
};
o.interval = setInterval(o.timer.bind(o), 1000);
}
DEMO
I hope it will be useful for someone.

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.

setInterval to loop through array in javascript?

I have a website where they want a news ticker. Currently, I have a array that populates it, and every x seconds, I want the news story to change.
function startNews(stories) {
}
I am aware that you can use setInterval, but it has to go through a new function and you can't specify certain javascript in the same function to fire when it does.
What are you suggestions?
Thanks!
You should use either setInterval() or repeated calls to setTimeout(). That's how you do something in javascript at some time in the future.
There are no limitations on what you can do with either of those timer functions. What exactly do you think you cannot do that is making you try to avoid them?
Here's a pseudo code example:
var newsArray = []; // your code puts strings into this array
var curNewsIndex = -1;
var intervalID = setInterval(function() {
++curNewsIndex;
if (curNewsIndex >= newsArray.length) {
curNewsIndex = 0;
}
setTickerNews(newsArray[curNewsIndex]); // set new news item into the ticker
}, 5000);
or it could be done like this:
var newsArray = []; // your code puts strings into this array
var curNewsIndex = -1;
function advanceNewsItem() {
++curNewsIndex;
if (curNewsIndex >= newsArray.length) {
curNewsIndex = 0;
}
setTickerNews(newsArray[curNewsIndex]); // set new news item into the ticker
}
var intervalID = setInterval(advanceNewsItem, 5000);
You should whenever possible use setTimeout. If your function takes longer to run than the interval, you can run into a constant 100% cpu usage situation.
Try this code:
http://jsfiddle.net/wdARC/
var stories = ['Story1','Story2','Story3'],
i = -1;
(function f(){
i = (i + 1) % stories.length;
document.write(stories[ i ] + '<br/>');
setTimeout(f, 5000);
})();
Replace document.write with your function.

changing the scope of an anonymous function on a setTimeout causes a weird warning

this has interested me purely as research and personal development. i have a namespaced set of functions / variables.
within 1 function I need to call another through setTimeout but keeping the scope to 'this'. i am struggling with this a little, can't seem to bind it for when the setTimeout runs.
var foo = {
ads: ["foo","bar"],
timeDelay: 3,
loadAds: function() {
var al = this.ads.length;
if (!al)
return; // no ads
for(var i = 0; i < al; i++) {
setTimeout(function() {
this.scrollAd(this.ads[i]);
}.apply(this), this.timeDelay * 1000);
}
},
scrollAd: function(adBlock) {
console.log(adBlock);
}
};
};
the .apply(this) DOES change the scope as the console.log outputs the right object back, but it runs the function immediately and then the exception/warning comes up as the callback remains empty:
useless setTimeout call (missing quotes around argument?)
is there an elegant way of doing this at all? i know i could do
var _this = this;
and reference _this in the anon callback. for example, in mootools i'd use .bind(this) instead...
and no, as this involves animating, i don't want to use " " around the string as it will need to be eval'd and would impact performance...
for(var i = 0; i < al; i++) {
setTimeout(function() {
this.scrollAd(this.ads[i]);
}.apply(this), this.timeDelay * 1000);
}
apply doesn't bind a function, it calls it. So you execute the scroll straight away and then pass its return value (undefined) to setTimeout, which is ineffective.
You probably meant to use a closure like this over this and the loop variable (which must be closed or it will be the same, post-loop value for every timeout):
for(var i = 0; i < al; i++) {
setTimeout(function(that, j) {
return function() {
that.scrollAd(that.ads[j]);
};
}(this, i), this.timeDelay * 1000);
}
However you may prefer to use the new ECMAScript Fifth Edition function binding feature, which has a much more compact syntax:
for (var i= 0; i<al; i++)
setTimeout(this.scrollAd.bind(this, this.ads[i]), this.timeDelay*1000);
(There's an implementation of function.bind for browsers that don't have have it natively at the bottom of this answer.)
From what I know you should indeed use something like this:
var self = this;
setTimeout(function(){self.scrollAd(ad);}, this.timeDelay * 1000);
But if you badly want to use .apply(), then do it like this:
var self = this;
setTimeout(function(){
function(){
}.apply(self);
}, this.timeDelay * 1000);
Also note that if you run this inside a for loop and use i's value inside a function that is run in timer, then your function will always run with the last value of i (i.e. i == al). In order to fix that, you'll need to make a closure with each value of i separately.
So taking your code and making it work it should look like this:
var foo = {
ads: ["foo","bar"],
timeDelay: 3,
loadAds: function() {
function runTimed(o, fn, args, time)
{
setTimeout(function(){ fn.apply(o, args); }, time);
}
var al = this.ads.length;
if (!al)
return; // no ads
for(var i = 0; i < al; i++) {
runTimed(this, this.scrollAd, this.ads[i], this.timeDelay*1000);
}
},
scrollAd: function(adBlock) {
console.log(adBlock);
}
};
};
Note: I haven't run this code so it may contain some mistakes.
Also if I were you, I'd use the data from object and don't pass it to the scrollAd (i is enough).

Categories