Как использовать setTimout в Coffeescript в цикле - PullRequest
7 голосов
/ 30 ноября 2011
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

В качестве упражнения приведенный выше код призван нарисовать небольшой рисунок на холсте, сделать паузу на секунду, а затем перерисовать его снова на 10 пикселей вправо.

Я вижу, что механика работает нормально, когда я прерываю цикл с предупреждением (как в последних двух закомментированных строках), но я не получаю ожидаемого поведения с помощью функции setTimeout.Дизайн просто появляется в крайнем правом положении после тайм-аута, пропуская промежуточные шаги между ними.

Я пробовал много разных способов сделать это из других примеров, но это просто растопило мой мозг.Есть предложения?

Ответы [ 2 ]

10 голосов
/ 30 ноября 2011

Джефф обрисовал в общих чертах один подход (используя setInterval и очистив его от обратного вызова), поэтому я выделю другой: использование setTimeout для обратного вызова.Что-то вроде

m = 10
do drawCallback = ->
  draw m, 150
  m += 10
  setTimeout drawCallback, 1000 unless m > 100

Обратите внимание, что между этими двумя подходами есть небольшая разница во времени: setInterval func, 1000 будет запускать функцию один раз каждые 1000 мс;цепочка setTimeout устанавливает задержку 1000 мс между каждым вызовом функции.Так что если бы draw потребовалось 100 мс, скажем, цепочка setTimeout была бы эквивалентна setInterval func, 1100.Это, вероятно, не имеет значения, но о нем стоит знать.

Бонусный подход : у вас нет , чтобы отказаться от цикла;вы можете просто установить все таймауты из него сразу:

for m in [10..100] by 10
  do (m) ->
    setTimeout (-> draw(m, 150)), 100 * m

do (m) необходим, чтобы при закрытии, переданном в setTimeout, было видно каждое значение m, а не только его окончательное значение впетля.См. Мою статью Вмешательство CoffeeScript для получения дополнительной информации об этом.

Наконец : Сначала я знаю, что все это кажется очень запутанным, но время в JS на самом деле очень простопотому что язык однопоточный.Это означает, что события, которые вы запланировали с помощью setTimeout или setInterval или любой другой асинхронной функции, никогда не произойдут во время цикла, даже если цикл бесконечный.Они появляются только после того, как весь ваш код завершит выполнение.Об этом я немного подробнее расскажу в моей книге о CoffeeScript .

2 голосов
/ 30 ноября 2011

Это может быть выражено более интуитивно как setInterval:

window.onload = ->

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

  ctx = document.getElementById("canvas").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)

  count = 10
  timer = setInterval (-> 
    if count == 100
        clearInterval(timer)
    draw(count, 150); count+=10
  ), 1000
...