Reputation: 901
I have looked through countless examples which indicate that this is supposed to work, but it does not. I was wondering if someone could have a look and indicate why. I am trying to access the variable "dia" from within the setTimeout function, but it always returns undefined:
var dialogue = new Array();
dialogue[0] = 'Hi there Mo, I am Mark. I will be guiding through the game for you today';
dialogue[1] = 'Hey there Mark, how you doing?';
dialogue[2] = 'I am doing fine sweetie pie, how about yourself?';
dialogue[3] = 'I am good too, thanks. Are you ready for today, i.e. the big day?';
dialogue[4] = 'I certainly am, Mark';
var dcount;
var loopDelay;
var diatext;
for(dcount = 0; dcount <= dialogue.length; dcount++) {
var dia = dialogue[dcount];
if(dcount == 0) { loopDelay = 0; } else {
loopDelay = ((dia.length)*1000)/18;
}
setTimeout(function() {
alert(dia);
diatext = Crafty.e('2D, DOM, Text')
.text(dia)
.textFont({ size: '11px', weight: 'bold' })
.attr({ x: 200, y: 150, w:400, h:300})
.css();
}, loopDelay);
}
Upvotes: 2
Views: 11059
Reputation: 96
try this
var dialogue = new Array();
dialogue[0] = 'Hi there Mo, I am Mark. I will be guiding through the game for you today';
dialogue[1] = 'Hey there Mark, how you doing?';
dialogue[2] = 'I am doing fine sweetie pie, how about yourself?';
dialogue[3] = 'I am good too, thanks. Are you ready for today, i.e. the big day?';
dialogue[4] = 'I certainly am, Mark';
var dcount;
var loopDelay;
var diatext;
for(dcount = 0; dcount < dialogue.length; dcount++) {
var dia = dialogue[dcount];
if(dcount == 0) { loopDelay = 0; } else {
loopDelay = ((dia.length)*1000)/18;
}
setTimeout(function(count) {
alert(dialogue[count]);
}, loopDelay,dcount);
}
This solution just pass an argument to the setTimeout function so it can take the array index from there and take the correct item
Upvotes: 1
Reputation: 1074355
There are two problems:
The first is that the function you're passing into setTimeout
has an enduring reference to the dia
variable, not a copy of dia
's value as of when the function was created. So when the functions run, they all see the same value for dia
, which is the value it has then, after the loop is complete.
This example may help make this clearer:
var a = 1;
setTimeout(function() {
alert(a);
}, 0);
a = 2;
setTimeout(function() {
alert(a);
}, 0);
The code above shows us "2" twice. It does not show us "1" and then "2". Both functions access a
as it is when they run.
If you think about it, this is exactly how global variables work. And in fact, there's a reason for that: It's exactly the way global variables work. :-)
More: Closures are not complicated
Now, sometimes, you want to get a copy of dia
's value as of when the function was created. In those cases, you usually use a builder function and pass dia
to it as an argument. The builder function creates a function that closes over the argument, rather than dia
:
for(dcount = 0; dcount <= dialogue.length; dcount++) { // But see note below about <=
var dia = dialogue[dcount];
if(dcount == 0) { loopDelay = 0; } else {
loopDelay = ((dia.length)*1000)/18;
}
setTimeout(buildFunction(dia), loopDelay);
}
function buildFunction(d) {
return function(d) {
alert(d);
diatext = Crafty.e('2D, DOM, Text')
.text(d)
.textFont({ size: '11px', weight: 'bold' })
.attr({ x: 200, y: 150, w:400, h:300})
.css();
};
}
Because the function buildFunction
returns closes over d
, which doesn't change, rather than dia
, which does, it gives us the value as of when it was created.
The second problem is that your loop condition is incorrect, which is why you're seeing undefined
. Your loop is:
for(dcount = 0; dcount <= dialogue.length; dcount++) {
There is no element at dialogue[dialogue.length]
. The last element is at dialogue[dialogue.length - 1]
. You should be exiting your loop with < dialogue.length
, not <= dialogue.length
. With < dialogue.length
, you'd still have a problem: dia
would always be the last entry (see above), but at least it wouldn't be undefined.
Upvotes: 11