Knowing when all other ready callbacks are done

2019-07-19 23:21发布

问题:

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.

回答1:

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.



回答2:

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


回答3:

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/



回答4:

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())();


回答5:

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