简体   繁体   中英

jQuery function calling itself with an each loop containing a setTimeout happens immediately rather than at the end

I'm on a project that has a need for a scrolling list of users online in a table. The idea is that the server is pinged for the entire list, it populates the first 5 and then removes the top and adds the next on a 2.5 second interval. After the entire list is gone through I need to hit up the server for the latest listing and repeat, forever.

The issue I'm having is the "forever" aspect. While the first load is fine and the loops works great, the second one has issues. If the //get_users_online_today(); is uncommented it appears as if the call takes places after the first setTimeout() trigger finishes.

I can't seem to figure out where to place the function inside the function to call it repeatedly.

$(function(){
   function get_users_online_today(){
      var user = 0;

      $.ajax({
         url: 'ajax.php',
         data: {action: 'Users Online Today'},
         dataType: 'json'
      }).done(function(users){
         $.each(users, function(index, details){
            if($('#users_online_today tbody tr').length <= 5){
               $('#users_online_today tbody').append('<tr><td>' + details.name + '</td></tr>');
            }else{
               setTimeout(function(){
                  $('#users_online_today tbody tr:first').remove();
                  $('#users_online_today tbody').append($('<tr><td>' + details.name + '</td></tr>').fadeIn(250));
               }, (user++) * 2500);
            }
         });

         //get_users_online_today();
      });
   }

   get_users_online_today();
});

Adding a closure around the setTimeout so that it correctly references the user that you're at in the loop should solve your problem. What's happening in your code now is that when the setTimeout actually occurs it's looking at the previously scoped "user" (the last user), instead of a locally scoped variable containing the correct value.

(function(i, d){
  setTimeout(function(){
    $('#users_online_today tbody tr:first').remove();
    $('#users_online_today tbody').append($('<tr><td>' + d.name + '</td></tr>').fadeIn(250));
  }, (i + 1) * 2500);
}(index, details));

You also don't really need that user variable, since you're passing the index of the user in the .each() function. Unless you're using that for another reason, in which case just set it to += 1 before/after the timeout function.

Here's a better explanation of what closures are and why they're useful: How do JavaScript closures work?

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