似乎无法弄明白,如何防止外循环继续,直到使用setTimeout执行内循环:
function $(s) { return document.getElementById(s); }
var i = 0;
for (i=0; i<6; i++){
$('t'+i).className = 'show';
cl('t'+i);
for (var j=0; j<50; j++){
cl('b'+i+'='+j+'%');
setTimeout(function(){ $('b'+i).style.width = j+'%';},200);
}
}
这一小段代码应首先使元素t0可见,然后以1%步长设置另一个元素b0的宽度,时间间隔为200ms,然后继续t1,b1,t2,b2等等
但是没有200ms延迟,整个代码立即执行。
---编辑---
我没有很好地解释,这就是我想要做的事情:
1. show element Ax
2. increase element Bx width by 1% every 200ms until 50%
3. wait until element Bx reaches 50% before continuing
4. increment x
5. goto 1
答案 0 :(得分:4)
两个问题:
i
和j
值i
和j
值主要问题是您传入setTimeout
的功能对i
和j
变量有持久的引用,不副本从创建函数时开始。这意味着所有函数在运行时都会看到i
和j
的值,分别为6
和50
。这称为“关闭”这些变量(该函数称为“闭包”)。
解决这个问题的常用方法是以一种不会改变的方式创建函数。有几种方法可以做到这一点;我最喜欢的是使用工厂功能:
function $(s) { return document.getElementById(s); }
var i = 0;
for (i=0; i<6; i++){
$('t'+i).className = 'show';
cl('t'+i);
for (var j=0; j<50; j++){
cl('b'+i+'='+j+'%');
setTimeout(makeHandler(i, j), 200);
}
}
function makeHandler(ivalue, jvalue) {
return function(){ $('b'+ivalue).style.width = jvalue+'%';};
}
现在我们调用 makeHandler
,它会向我们返回一个关闭ivalue
和jvalue
的函数,该函数不会更改。或者上面的改进,让我们在完成它时处理制造商功能:
function $(s) { return document.getElementById(s); }
var i = 0;
var makeHandler = function(ivalue, jvalue) {
return function(){ $('b'+ivalue).style.width = jvalue+'%';};
};
for (i=0; i<6; i++){
$('t'+i).className = 'show';
cl('t'+i);
for (var j=0; j<50; j++){
cl('b'+i+'='+j+'%');
setTimeout(makeHandler(i, j), 200);
}
}
makeHandler = undefined;
如果你可以依赖ES5功能(由于你所针对的环境,或者因为你已经包含了ES5垫片),你可以使用新的Function#bind
获得相同的效果。 Function#bind
创建了一个新函数,就像makeHandler
一样,但引擎总是有可能优化一点:
function $(s) { return document.getElementById(s); }
var i = 0;
for (i=0; i<6; i++){
$('t'+i).className = 'show';
cl('t'+i);
for (var j=0; j<50; j++){
cl('b'+i+'='+j+'%');
setTimeout(handler.bind(undefined, i, j), 200);
}
}
function handler(){
$('b'+ivalue).style.width = jvalue+'%';
}
bind
的第一个参数是this
在函数中应该是什么;在我们的例子中我们不关心,所以我已经指定了undefined
(这意味着该函数将在浏览器上引用this
引用全局对象 - window
- 除非这是严格模式代码,在这种情况下,this
实际上是undefined
)。
您的所有功能都计划在上述代码后运行200ms。所以他们会。 :-)如果你想将它们分开,你需要为每次setTimeout
的呼叫增加200ms。我们可以乘以i
和j
:
setTimeout(makeHandler(i, j), (200 * i * j) + 200);
现在第一个将在200ms之后运行,第二个将在200ms之后运行,等等。整个过程将需要大约一分钟才能完成。这假设您希望第一个元素增长,然后是下一个元素,然后是下一个元素,而不是所有六个元素彼此平行增长。
或者,您可能希望让每个函数调用其后继函数。这可能就是我要做的。因此,不是安排300个函数调用,只需安排一个函数调用,当它发生时,安排下一个:
function $(s) { return document.getElementById(s); }
// Our counters are here
var i = 0, j = 0;
// This handles the outer portion of the loop (mostly)
function outer() {
$('t'+i).className = 'show';
cl('t'+i);
j = 0;
// Schedule the first inner portion 200ms from now
setTimeout(inner, 200);
}
// This handles the inner portion of the loop (mostly)
function inner() {
// Do this bit
$('b'+i).style.width = j+'%';
++j;
if (j < 50) {
// Continue the inner loop in 200ms
setTimeout(inner, 200);
}
else {
// Set up next outer loop
j = 0;
++i;
if (i < 6) {
// Not done yet, keep going
setTimeout(outer, 200);
}
}
}
// Kick it off
setTimeout(outer, 200);
在上文中,我也提到了这些方面:
$('t'+i).className = 'show';
cl('t'+i);
...进入延迟代码,我怀疑是合适的。
答案 1 :(得分:3)
这是因为你不关闭了i
和j
:
setTimeout(function(i, j) {
return function() {
$('b'+i).style.width = j+'%';
}
}(i, j), 200);
答案 2 :(得分:0)
setTimeout(function () { alert('Refreshing…'); }, 2000);
这意味着在两秒钟(2000毫秒)后显示警告