How to add a kill method to an existing jQuery plugin?

I'm using the plugin code posted at:

jQuery Tips and Tricks

... which is essentailly an asynchronous timer loop:

jQuery.forEach = function (in_array, in_pause_ms, in_callback)
{
    if (!in_array.length) return; // make sure array was sent

    var i = 0; // starting index

    bgEach(); // call the function

    function bgEach()
    {
        if (in_callback.call(in_array[i], i, in_array[i]) !== false)
        {
            i++; // move to next item

            if (i < in_array.length) setTimeout(bgEach, in_pause_ms);
        }
    }

    return in_array; // returns array
};


jQuery.fn.forEach = function (in_callback, in_optional_pause_ms)
{
    if (!in_optional_pause_ms) in_optional_pause_ms = 10; // default

    return jQuery.forEach(this, in_optional_pause_ms, in_callback); // run it
};

With the help of this great community, I have learned that if I "return true;" or "return false;" it has the same affect of moving on to the next iteration of the loop.

What I'd like to do is also programmatically kill (destroy) the $.forEach loop call entirely, not allowing the remaining iterations to run.

How might I add a method to this plugin to allow me to kill a specific call to the plugin while not affecting other calls concurrently running calls?

I've thought about assigning the call to a variable like so:

var foo = $.forEach(json, 1000, function(idx,item) { /* do stuff */ });

...to reference specific instances later without affecting others, but how do I add a kill method and invoke it on foo? I need the equivalent of foo.abort() that's used with ajax calls.

Can it be done? If so, how?

Answers


This is really just example code, so it has some set of issues.

You could alleviate this by simply adding an expando property to the in_array object:

jQuery.forEach = function (in_array, in_pause_ms, in_callback)
{
    // Note, you should use $.isArray(), which is smarter than checking length.
    if (!$.isArray(in_array)) return false;
    in_array.valid = true;
    var i = 0; // starting index
    function bgEach()
    {
        if (!in_array.valid) return;
        if (in_callback.call(in_array[i], i, in_array[i]) !== false)
        {
            i++; // move to next item

            if (i < in_array.length) setTimeout(bgEach, in_pause_ms);
        }
    }

    // I moved this because you were calling it before it was defined.
    bgEach(); // call the function
    return in_array; // returns array
};

Since you're getting the raw array object back from the method, you can just set the 'valid' property of the object to false when you're done:

var foo = $.forEach(json, 1000, function(idx,item) { /* do stuff */ });
foo.valid = false;

Since the valid property cancels the setTimeout recursion, this will effectively kill the looping.

Note that this kind of code is somewhat dangerous. You'll run into race conditions and whatnot, but that's what happens when you start doing pseudo-threading & async code in JS.


You could pass the timer into the callback and change

if (i < in_array.length) setTimeout(bgEach, in_pause_ms);

to

if (i == in_array.length) clearTimeout(timeout);

This will guarantee that it will only loop as many times as there are items.

Here is the jQuery forEach:

jQuery.forEach = function (in_array, in_pause_ms, in_callback)
{
    if (!in_array.length) return; // make sure array was sent

    var i = 0; // starting index

    bgEach(); // call the function

    function bgEach(){
        var timeout;
        if (in_callback.call(in_array[i], i, in_array[i], timeout = setTimeout(bgEach, in_pause_ms)) !== false)
        {
            i++; // move to next item
            // if end of array, stop iterating
            if (i == in_array.length) clearTimeout(timeout);
        }
    }

    return in_array; // returns array
};

You could use this to clear the timer if it met certain criteria, thereby ending the loop. Example:

$.forEach([1,2,3], 1000, function(idx, item, timer) {
  if(item == 2) {
    clearTimeout(timer);
  }
});

Need Your Help

How to right dealloc object when app goes to background?

iphone memory-management ios4 multitasking dealloc

Have only one question "How to right dealloc object when app goes to background ?". I'm working on some app, everything works great and fine. When i put app to background and then start it again i...