如何在循环中的Coffeescript中使用setTimout

use*_*090 7 javascript jquery settimeout coffeescript

window.onload = ->

  boxOrig1 = 10
  boxOrig2 = 30
  canvasW = 400
  canvasH = 300

  ctx = $("#canvas")[0].getContext('2d');

  draw = (origin,dimension) ->    
    ctx.clearRect(0, 0, canvasW, canvasH)
    ctx.fillStyle = 'rgb(200,0,0)'  
    ctx.fillRect(origin + boxOrig1, boxOrig1, dimension, dimension)  
    ctx.fillStyle = 'rgba(0, 0, 200, 0.5)'  
    ctx.fillRect(origin + boxOrig2, boxOrig2, dimension, dimension)

  for m in [10..100] by 10
    t = setTimeout (-> draw(m, 150)), 1000 
    t.clearTimeout
#     draw(m,150)
#     alert m
Run Code Online (Sandbox Code Playgroud)

作为练习,上面的代码是在画布上绘制一个小设计,暂停一秒,然后再向右重绘10个像素.

我可以看到,当我用警报中断循环时,机制工作正常(如最后两条注释行),但我没有得到setTimeout函数的预期行为.设计只是在超时后出现在最右边的位置,跳过两者之间的增量步骤.

我从其他例子中尝试了许多不同的方法,但它只是在融化我的大脑.有什么建议?

Tre*_*ham 10

Geoff概述了一种方法(使用setInterval并从回调中清除它),因此我将概述另一种方法:使用setTimeout回调.就像是

m = 10
do drawCallback = ->
  draw m, 150
  m += 10
  setTimeout drawCallback, 1000 unless m > 100
Run Code Online (Sandbox Code Playgroud)

请注意,您应该注意的两种方法之间存在细微的时序差异:setInterval func, 1000每1000ms运行一次该函数; 链接setTimeout将在每个函数调用之间延迟1000ms.因此,如果draw花了100毫秒,那么链式setTimeout就等于setInterval func, 1100.这可能没关系,但值得注意.

奖励办法:你不必须放弃你的循环; 你可以立刻设置它的所有超时:

for m in [10..100] by 10
  do (m) ->
    setTimeout (-> draw(m, 150)), 100 * m
Run Code Online (Sandbox Code Playgroud)

do (m)是必要的,以便闭包传递以setTimeout查看每个值m,而不仅仅是循环中的最终值.有关详细信息,请参阅我的文章A CoffeeScript Intervention.

最后:我知道这一切看起来都很混乱,但JS中的时间实际上非常简单,因为语言是单线程的.这意味着即使循环是无限的,循环期间也不会发生您使用setTimeout或者setInterval任何其他异步函数调度的事件.它们仅在所有代码完成执行后发生.我在关于CoffeeScript的书中更详细地讨论了这个问题.