2

I want my handler of the ready event will fire after all other handlers are done.
It's extremely handy for manipulating plugins' undesired actions.

If I write my handler after all others, it only guarantees it will fire after all others fired, not finished:

$(function() {
    setTimeout(function() { alert('other handler'); }, 500);
});


$(function() { alert('my handler'); });​

Fiddle

In that code, my handler alerted first.

I read that before jQuery version 1.4 the readyList was public. so in version 1.7 I have no idea how I can tell that my handler is the last handler or not.

gdoron is supporting Monica
  • 136,782
  • 49
  • 273
  • 342
  • take a look at http://api.jquery.com/deferred.promise/ – Kris Ivanov Mar 06 '12 at 10:53
  • If the idea is that you don't control the other ready handlers, then given your example where another handler used a `setTimeout`, you can never actually know *(without inspecting the other code)* if your code will run after all other code. The `readyList` wouldn't help even if it was public, because in your example, the handler with the `setTimeout` will be removed from the ready list long before the `setTimeout` handler runs. The `readyList` Array doesn't have any control over that sort of asynchronous code either. Hope I've understood your question correctly. –  Mar 06 '12 at 14:19
  • @gdoron, have you tried some of the answers given? – Fabrizio Calderan loves trees Mar 06 '12 at 14:20
  • @amnotiam. Yes you have. So do you have a solution for this kind of scenarios? There isn't really a `setTimeout` in the external code, but that code might takes a "long" time until it's finished. It has many lines of code, selectors and callbacks. So I can't be sure that my code will run after the plugin finished. Any Ideas? – gdoron is supporting Monica Mar 06 '12 at 14:25
  • 1
    If you don't control *(can't modify)* the other code, then I really don't have a solution. But if the other code is just long running, but not *asynchronous*, then there wouldn't be any issue, because if your code is the last `.ready()` handler assigned, it shouldn't matter how long the other handlers take to execute. If their code as *synchronous*, it will force yours to wait until they're complete. It's just that if they're *asynchronous*, like your `setTimeout` example, then there's nothing you can do short of examining the other code, and modifying yours to make sure it fires last. –  Mar 06 '12 at 14:32
  • @amnotiam. Can you please copy paste your comments to an answer? Why do you keep writing excellent answers in comments? **=)** – gdoron is supporting Monica Mar 06 '12 at 14:35
  • Sure I'll post it. :) Just wanted to be sure I was understanding your situation first. –  Mar 06 '12 at 14:36
  • This is probably not a good idea, but one thing your code could do would be to replace all asynchronous methods in JavaScript with a substitute that does some logging before calling the original method. You'd need to do the swap before any other code runs, but then you could have a log of how many times `setTimeout` *(for example)* was called before your code, and what durations were passed to it. Then your code could use that data to make sure it comes last. It's a pretty intrusive approach, but would work *(unless some other made similar modifications that would counter yours).* –  Mar 06 '12 at 14:47
  • @amnotiam. How can you modify callbacks, eg': `$(...).fadeIn('slow', function(){foo();});` – gdoron is supporting Monica Mar 06 '12 at 15:01
  • You mean to use a solution like I posted in my previous comment? –  Mar 06 '12 at 15:03
  • @gdoron: You could either use the approach I described to hack jQuery and replace certain methods *(like animations)* with ones that wrap and invoke the original, then use that knowledge to see how long the animation takes, and whether there was a callback. Of course a callback could begin a new animation that itself has a callback, so you'd need to keep tracking until you see that no more animations have started. This wrap and replace approach is what the `livequery` plugin does to track DOM changes made by jQuery methods. It'll be a little tricky, and you may find it not worth the effort. –  Mar 06 '12 at 15:10

5 Answers5

2

If the idea is that you don't control the other ready handlers, then given your example where another handler used a setTimeout, you can never actually know (without inspecting the other code) if your code will run after all other code.

The readyList wouldn't help even if it was public, because in your example, the handler with the setTimeout will be removed from the readyList long before the setTimeout handler runs. The readyList Array doesn't have any control over that sort of asynchronous code either.

So if you don't control (can't modify) the other code, then I really don't have a solution. But if the other code is just long running, but not asynchronous, then there wouldn't be any issue, because if your code is the last .ready() handler assigned, it shouldn't matter how long the other handlers take to execute. If their code is synchronous, it will force yours to wait until they're complete. It's just that if they're using asynchronous code, like your setTimeout example, then there's nothing you can do short of examining the other code, and modifying yours to make sure it fires last.

0

I usually use the following pattern, simply keepig a counter of finished async functions:

var fired = 10;
var finished = 0;

for (var i = 0; i < fired; i++) {    
    // Call an asynchronous function 10 times
    async_function(function() {
        // When asynchronous function finishes,
        // we check if it was the last one.
        if (++finished == fired) all_ready();
    });
}

The same in coffeescript:

fired = 10
finished = 0
(async_function -> all_ready() if ++finished == ready) for n in [0...fired]

(We call the same function for 10 times to keep the example simple, while in reality you may of course call different functions, but the same idea apply; in callback function you check the counter.)

jholster
  • 4,786
  • 1
  • 23
  • 20
0

You can use something like this:

function Join(cb) {
    var paths = 0;
    var triggerCallback = cb;

    this.add = function () {
        paths ++;
        return this.call;
    };

    this.call = function () {
        paths --;
        if (paths == 0)
            if (triggerCallback)
                triggerCallback();
    };

    return this;
}

An example:

function finishedAll() {
    alert("All finished");
}

window.join = new Join(finishedAll);

function sampleCall(callJoinHandle) {
    alert("Not done yet.");
    if (callJoinHandle) callJoinHandle();
}

var cb1 = join.add();
setTimeout(function () { sampleCall(cb1); }, 1000);

var cb2 = join.add();
setTimeout(function () { sampleCall(cb2); }, 1000);

var cb3 = join.add();
setTimeout(function () { sampleCall(cb3); }, 1000);
Ioannis Karadimas
  • 7,296
  • 3
  • 32
  • 45
0

An idea could be creating an array of deferred to use inside every ready function (except the last one), resolving each one when the snippet has completed.

Then, in the last ready function you could simply check the promise resolution with $.when and then execute some other code: e.g.

var dfdArray = [];

$(function() {
    var dfd = $.Deferred();
    dfdArray.push(dfd);
    setTimeout(function() { 
      console.log('another simple handler'); 
      dfd.resolve(); 
    }, 2000);
});


$(function() {
    var dfd = $.Deferred();
    dfdArray.push(dfd);
    setTimeout(function() { 
        console.log('first handler'); 
        dfd.resolve(); 
    }, 1200);
});


$(function() {
    $.when.apply($, dfdArray).done(function() {
      alert('my final handler');
    })
});

See fiddle in action here: http://jsfiddle.net/DXaw5/

Fabrizio Calderan loves trees
  • 109,094
  • 24
  • 154
  • 160
0

I don't know if it is possible for you to create a queue for all the functions like

var queue = [];
queue .push(fun1);
queue .push(fun2);

//execute the first function and remove it.
(queue .shift())();
user160820
  • 13,598
  • 21
  • 57
  • 91