简体   繁体   中英

Update DOM while iterating over Backbone Collection?

So I am trying to display a loading bar when rendering very large collections. I have a placeholder for the loading bar when the page initially loads, and I am trying to update it like this:

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

The problem here is that the DOM isnt updated until the addAll function completes. I have a feeling that this is me not understanding some basic JS fundamentals. Any help would be greatly appreciated!

Yes, you are missing something fundamental: the browser won't do any of its own work until your code returns control to the browser.

Consider some code like this:

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')

You'll see a brief pause and then #bar and #n will be updated and done will appear in the console. Demo: http://jsfiddle.net/ambiguous/f5qKV/ (you might need to increase the 1000 to make things more obvious).

However, if you return control to the browser with setTimeout(..., 0) on each iteration:

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)

you'll be able to see #bar and #n changing and then you'll see done in the console when everything has finished. Demo: http://jsfiddle.net/ambiguous/UCbY8/1/

Note that the setTimeout version uses the setTimeout callback to trigger the next timeout, that ensures that everything happens in the same order as they would in a simple for or each loop.

The lesson is that you have to add some old-school pseudo cooperative multi-tasking hacks into the mix if you want to use that kind of progress indicator.


Handing control back to the browser also leaves you open to user interaction when you're not expecting it. You might want to add a general UI blocker to keep people from clicking on things while you're working if you go this route.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM