1

所以我试图在渲染非常大的集合时显示一个加载栏。当页面最初加载时,我有一个加载栏的占位符,我正在尝试像这样更新它:

addAll: 
  @collection.each(((obj, index) ->
    @addOne(obj, index)), this
  )

addOne: (obj, index) ->
  percent_complete = ((index / @collection.length) * 100)
  $(".loading_bar").width("#{percent_complete}%")
  # Proceed with rendering, create the view, etc

这里的问题是 DOM 在addAll函数完成之前不会更新。我有一种感觉,这是我不了解一些基本的 JS 基础知识。任何帮助将不胜感激!

4

1 回答 1

3

是的,您缺少一些基本的东西:在您的代码将控制权返回给浏览器之前,浏览器不会做任何自己的工作。

考虑一些这样的代码:

collection = [1..1000]

addOne = (index) ->
  $('#n').text(index + 1)
  percent_complete = ((index + 1) / collection.length) * 100
  $("#bar").width("#{percent_complete}%")

addOne(i) for e,i in collection  
console.log('done')

您会看到短暂的停顿,然后#bar#n更新并done出现在控制台中。演示: http: //jsfiddle.net/ambiguous/f5qKV/(您可能需要增加 1000 以使事情更明显)。

但是,如果您setTimeout(..., 0)在每次迭代时将控制权返回给浏览器:

collection = [1..1000]

addOne = (index) ->
  $('#n').text(index + 1)
  percent_complete = ((index + 1) / collection.length) * 100
  $("#bar").width("#{percent_complete}%")

i = 0
timeOut = ->
    if(i == collection.length)
        console.log('done')
        return
    addOne(i++)
    setTimeout(timeOut, 0)
setTimeout(timeOut, 0)

您将能够看到#bar#n更改,然后done当一切完成后您将在控制台中看到。演示:http: //jsfiddle.net/ambiguous/UCbY8/1/

请注意,该setTimeout版本使用setTimeout回调来触发下一次超时,以确保一切都以与简单foreach循环中相同的顺序发生。

教训是,如果你想使用那种进度指示器,你必须添加一些老式的伪合作多任务处理技巧。


将控制权交还给浏览器也会让您在不期望的情况下接受用户交互。如果你走这条路,你可能想要添加一个通用的 UI 阻止程序,以防止人们在你工作时点击东西。

于 2012-11-05T18:36:04.873 回答