循环内的 setTimeout() 方法

Posted

技术标签:

【中文标题】循环内的 setTimeout() 方法【英文标题】:setTimeout() method inside a while loop 【发布时间】:2016-10-10 05:42:26 【问题描述】:

我已经阅读了关于 w3schools 的相关页面和其他类似问题,但似乎无法理解以下内容有什么问题:

var myfunc03 = function (i) 
  document.getElementById('d01').innerhtml += 100-i+"<br>";
;

var myFunc01 = function() 
  i=0;
  while (i<100) 
    setTimeout(myfunc03(i), 1000)
    i++;
  
;

myFunc01(); 运行时。

没有任何停顿,i 的所有可能值都会立即列出。

这里有逻辑错误吗?

【问题讨论】:

这当然突出了他的第二个错误——从闭包中访问修改后的变量 (i)。当超时触发时,i 将更改为最后一个。 setTimeout 期望 Function 作为第一个参数,但您传递的是 myfunc03result (这是 underfined,因为您正在调用它) 【参考方案1】:

while 循环不会等待 setTimeout() 完成。您需要为每个设置不同的时间延迟以在不同的时间执行它们,并使用闭包来保存i 的值。同样在您的情况下,函数将首先执行,并且返回值设置为 setTimeout() 中的参数,因此您需要在匿名函数中调用该函数或直接设置该函数。

var myFunc01 = function() 
  var i = 0;
  while (i < 100) 
    (function(i) 
      setTimeout(function() 
        document.getElementById('d01').innerHTML += 100 - i + "<br>";
      , 1000 * i)
    )(i++)
  
;

myFunc01();
&lt;span id="d01"&gt;&lt;/span&gt;


虽然这里可以使用setInterval()

var myFunc01 = function() 
  var i = 0;
  // store the interval id to clear in future
  var intr = setInterval(function() 
    document.getElementById('d01').innerHTML += 100 - i + "<br>";
    // clear the interval if `i` reached 100
    if (++i == 100) clearInterval(intr);
  , 1000)



myFunc01();
&lt;span id="d01"&gt;&lt;/span&gt;

【讨论】:

详尽的解释。我遇到了同样的问题。很好的例子,谢谢!【参考方案2】:

您可以使用递归更简单做到这一点:

var i = 0;
function f1()  ... ;   
function f() 
    f1();
    i += 1;
    setTimeout(function() 
        if(i < 100) 
            f();
        
    , 1000);

f();

示例

var i = 0;

var myfunc03 = function(i) 
  document.getElementById('d01').innerHTML += 100 - i + "<br>";
;

var myFunc01 = function() 
  myfunc03(i);
  i += 1;
  setTimeout(function() 
    if (i < 100) 
      myFunc01();
    
  , 1000);


myFunc01();
&lt;div id="d01"&gt;&lt;/div&gt;

一个可重用的函数

function say(sentence) 
  console.log(sentence);


function sayHello() 
  say("Hello!");


var fn = sayHello;
var count = 10;
var ms = 1000;

function repeat(fn, count, ms) 
  var i = 0;

  function f() 
    fn();
    i += 1;
    setTimeout(function() 
      if (i < count) 
        f();
      
    , ms);
  

  f();


repeat(fn, count, ms);

【讨论】:

【参考方案3】:

while 等待setTimeout

(async () => 
  var i = 0;
  while (await new Promise(resolve => setTimeout(() => resolve(i++), 1000)) < 100) 
    console.log("I get printed 100 times every second");
  
)();

【讨论】:

【参考方案4】:

是的。您的代码中有两个问题:

    setTimeout 函数接受一个函数作为第一个参数,但在您的代码中,myfunc03(i) 不返回任何内容 while 循环不能满足你的需要,你必须使用递归函数。因为第二个函数应该在第一个超时触发后调用。

示例代码:

var myfunc03 = function (i) 
  setTimeout(function() 
    document.getElementById('d01').innerHTML += 100-i+"<br>";
    if (i < 100) 
      i++;
      myfunc03(i);
    
  , 1000);
;

var myFunc01 = function() 
  myfunc03(0);
;

myFunc01();
&lt;div id="d01"&gt;&lt;/div&gt;

【讨论】:

【参考方案5】:

我认为您在 setTimeout 上缺少分号,您应该尝试按以下方式传递参数:

setTimeout(myfunc03, 1000*i, i); 

【讨论】:

对于现代浏览器来说,这是最好的解决方案,因为它避免了闭包的尴尬。但是,第二个参数应该是1000 * i;否则,您同时将 100 个超时设置为 1 秒。比较 jsfiddle.net/ofpp26or/2 和 jsfiddle.net/ofpp26or 是的,没错。我打算只显示语法,因为这可能会导致 js 错误。他可能会根据他的逻辑要求使用时间参数。【参考方案6】:

一个简单的选择是改用setInterval() 方法。 setInterval 将每次循环执行代码,直到您使用 clearInterval() 方法将其中断。

示例

var index = 0;
let incrementEveryHalfSecond = setInterval(function()
  index++;
  document.querySelector("body").innerHTML += index+'</br>';
  if(index == 10) clearInterval(incrementEveryHalfSecond)
, 500) 
&lt;body&gt;&lt;/body&gt;

请注意,您决定中断循环,将存储 setInterval 方法的变量作为参数传递给 clearInterval 方法。现在您可以创建函数以获得更好的清晰代码。 w3schools documentation

问题回答:[从 100 到 1] 我倒了 i 的顺序来简化,但你也可以使用“100-i”。

var myfunc03 = function (i) 
  document.getElementById('d01').innerHTML += i+"<br>";
;

var myFunc01 = function() 
  let i=100;
  let incrementEveryOneSecond = setInterval(function()
    myfunc03(i);
    if(--i == 0) clearInterval(incrementEveryOneSecond);
  , 1000) 
 
;
myFunc01();
 &lt;span id="d01"&gt;&lt;/span&gt;

【讨论】:

【参考方案7】:

while 方法运行速度很快,所有超时几乎都在第一秒后执行。你可以做的是

    而不是 while 从 myfunc03 调用 $timeout 以获得下一个值 在您的 while 呼叫超时内,随着秒数的增加,例如 i*1000

另外,正如其他人指出的那样,您不能使用来自 setTimeout 的参数调用函数 使用匿名函数,如

...
while (i<100) 
  setTimeout(
     function(i)
        myfunc03(i);
     , i*1000);
   i++;

...

为此

【讨论】:

请继续努力。这既不可读也不可理解 @mplungjan 是的,我的错! 您没有解决关闭问题 我知道,其他人已经这样做了,最初我错过了。所以我只是修改了我原来的答案。抄袭别人的答案感觉不对:) @mplungjan 没关系。我不只是为了选票或声誉而写东西。只是消磨一些空闲时间并帮助其他编码人员:) 无论如何,按照你的坚持纠正答案

以上是关于循环内的 setTimeout() 方法的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript:了解 for 循环内的 let 范围 [重复]

Javascript中setTimeout()以及clearTimeout( )的使用

setTimeout()和setInterval()的用法

关于setTimeout的妙用

JS里设定延时:js中SetInterval与setTimeout用法

js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)