4

What is the best/recommended manner in which to insert a 'pause' with each enumeration over a set of elements using the jQuery '.each()'?

$( '.someClass' ).each( function() {
    $( this ).trigger( 'click' ); //fire some request that appends a new div on the body 
    //wait for div to be appended to the DOM.  perhaps use the pause here
    //execute code once the div has been appended 
});
stavarotti
  • 552
  • 5
  • 19
  • 1
    This will be easier to answer if you give us some context of what you are doing, as the solution will vary depending on that. – Gazler Jul 18 '11 at 21:20

6 Answers6

3

Technically, you can't do this as you have modeled in your code, since JavaScript is single-threaded, and usually runs on the browser (or tab) UI thread -- any sleep/delay will block the browser from redrawing the page and will also prevent user interaction with the page.

You need to arrange for the browser to invoke your code periodically. Something like this will do the trick:

var objects = $.makeArray($( '.someClass' ));

var callback;

callback = function() {
    var item = objects.shift();

    // Do something with item.

    if (objects.length != 0) {
        setTimeout(callback, 5000);
    }
}

setTimeout(callback, 5000);

See an example.

This solution assumes that you want each item to be processed 5 seconds from the time that the last item finished processing. This means that:

  1. If you do confirm() or something while processing each item, the next item will be processed 5 seconds from the time that the user closes the dialog.
  2. The total execution time will be (5000N + T) where N is the number of items in the initial list, and T is the total time it takes to process each item.

Here's a function you can use that encapsulates this functionality:

jQuery.eachWithDelay = function(sequence, delay, callback) {
    var objects = jQuery.makeArray(sequence);

    if (objects.length == 0) {
        return;
    }

    var f;

    f = function() {
        var item = objects.shift();

        if (callback(item) && objects.length != 0) {
            setTimeout(f, delay);
        }
    };

    setTimeout(f, delay);
};

Called like:

$.eachWithDelay($('.someClass'), 5000, function(item) {
    // Do something with item

    return true; // Return true to continue iterating, or false to stop.
});

See the updated fiddle.

cdhowie
  • 133,716
  • 21
  • 261
  • 264
  • 1
    Just a note - `Array.shift()` in javascript can be significantly slower than manually iterating through the array. This is because javascript arrays are not 'true' arrays - they are objects with Strings as keys. This means that when you `.shift()` the first element off, the JS engine must move every item in the array, eg. `arr["0"] = arr["1"]; arr["1"] = arr["2"]; ...etc...` – digitalbath Jul 18 '11 at 21:40
  • @digitalbath: Noted. Of course, premature optimization is the root of all evil; if that optimization is required in this case then it could be implemented pretty easily. I tend to go for readability first, which is why I chose `shift`. But readers should be made aware of the performance implications, so +1 to you. – cdhowie Jul 18 '11 at 21:43
  • totally agree - just thought I'd mention it. – digitalbath Jul 18 '11 at 22:52
0

There is no good way to put a delay like that in the middle of a javascript function.

To legitimately delay for 5 seconds before the next increment of work, you need to acquire the data you need for the job, break the work into small chunks, set a 5 second timer and do one chunk of the work on each timer tick.

Here's a working example of one way to do that: http://jsfiddle.net/jfriend00/tgBYk/

var objList = $('.someClass');
var cntr = 0;
var timeDelay = 5000;

function doNextIteration() {
    $(objList[cntr++]).html(cntr);   // your code here: This is the progress I'm showing in my demo
    if (cntr < objList.length) {
        setTimeout(doNextIteration, timeDelay);
    }
}

if (objList.length > 0) {
    doNextIteration();
}
jfriend00
  • 580,699
  • 78
  • 809
  • 825
0

if you have the html like

<div class="someClass">google</div>
<div class="someClass">facebook</div>
<div class="someClass">ARPANET</div>

wrap it in $(document).ready() jsfiddle does it automatically

$(".someClass").each(function(i){

       $(this).delay(i+"1000").fadeOut("slow");

    });

here is the fiddle http://jsfiddle.net/J6Mw6/1/

you can set the time of your choice

or maybe this can help according to your edited question

http://jsfiddle.net/J6Mw6/2/

Rafay
  • 30,236
  • 5
  • 64
  • 98
  • If I understand the jQuery .delay function, it puts things in a queue and will only work with certain types of operations that use that queue. Since the OP didn't say what they're really trying to do inside the loop, we can't know if this would actually work or not. – jfriend00 Jul 18 '11 at 21:42
0

If you must use .each for some reason, then this will get you pretty close, provided you're not doing a ton of work inside each iteration:

var i = 0;
$( '.someClass' ).each( function() {
    setTimeout((function (el) {
        // do something with 'el'
    })(this), i * 5000);
    i += 1;
});

But it would be more correct (given the wording of your question) to do something like this:

(function() {
    var arr = $('.someclass'),
        i = 0,
        fn = function() {
            var el = arr[i];

            // do something with 'el'

            i += 1;
            if (i < arr.length) {
                setTimeout(fn, 5000);
            }
        };
    fn();
})();
digitalbath
  • 6,160
  • 2
  • 15
  • 15
0

Since we're just throwing answers around it seems; heres mine. No each?

function doTheDo(els) {

 if(els.length == 0) return;

 $(els.shift()).click();
 setTimeout(doTheDo, 5000, els);

}

doTheDo($( '#errors li' ).toArray());

Javier Buzzi
  • 4,313
  • 28
  • 40
  • You can always go backwards by swapping the .shift() to a .pop() – Javier Buzzi Jul 18 '11 at 21:52
  • Damn it, i cant reply to @digitalbath so i'll do it here. You're wrong, at least in Chrome, pop is 16x faster than for() and almost 66x faster than each() : proof : http://jsperf.com/pop-vs-loop – Javier Buzzi Jul 18 '11 at 22:43
-4

setTimeout(yourFunction(),5000); would probably be the thing you want. But remember that this function will execute yourFunction() after 5s. If there is code after your setTimeout then it will still execute right away, so it depends on what you want to do. If you really want to halt all execution of all of your code, then it's a harder problem to solve. You can take a look at this document for some proposed solutions/functions. But with JavaScript it's hard to completely halt your execution without completely locking up your CPU.

vinceh
  • 3,392
  • 1
  • 18
  • 23
  • 1
    This is a very bad practice. It locks up the browser (UI is completely unresponsive) and maxes the CPU for the whole time. Some browsers may even report that the web page has hung to protect the viewer from errant javascript code. – jfriend00 Jul 18 '11 at 21:39