Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for event to be handled to continue executing the function that has triggered the event.

I'm looking for an elegant, efficient solution to my problem :

I have this webapp with many components;

One main component include many additions that will grow/evolve with time.

This main component has a function in which before actually doing what it is supposed to do, it is triggering an event beforedo so that the addons can listen to.

dostg : function () {
   $doc.trigger('beforedo');

   //do stuff but after event is handled by the addons ?

}

In the addons codes

$doc.on('beforedo',function(e) {
   //do before addon stuff
}

Now those before doing stuff may involve ajax request or anything that would take some processing time.

I could increment a counter on ajax requests and wait it is down to nil, but what I'd like is a solution that would just wait for all handlers to finish their job (and therefore that'd work whatever is being done in those addons handlers).

Is there some miracle solution to do this or I have to forget about event in this situation and go another way (array of function to iterate, addons pushing new function into array) ?

Thanks for your expertise!

-------EDIT FOLLOWING THE BOUNTY

Apologize to @xbonez & @Sergiu Paraschiv , I should have edit the question with the solution I'm using now before offering the bounty (solution I'm not totally satisfied with, hence the bounty).

//app
var $doc = $(document.body); 


//component    
$doc.on({
    wait: function (e) { ++$doc.component.handleinprogress; },

    addonready: function () {
        --$doc.component.handleinprogress;
        if ($doc.component.handleinprogress==0) $doc.trigger('readyfordo');
    },

    readyfordo: function () {
        //do stuff after event has been handled by the addons 
    }        
});

$doc.component = {
    handleinprogress: 0,
    dostg: function () {

        $doc.component.handleinprogress = 0;
        $doc.trigger('beforedo');
    }
};

//in component addon files

$doc.on('beforedo', function (e) {

    $doc.trigger('wait');
    //do addon stuff
    $doc.trigger("addonready");
}

I'm not satisfied with this solution because even if I don't need to do stuff in the addon beforedo I still have to add the handler to trigger addonready (in at least one of the addon -> so either i lose flexibility to add/remove addon from the component without worrying whether readyfordo get triggered, either I have to include the handler in every addon - mostly -75%- for nothing).

like image 295
mikakun Avatar asked May 16 '13 07:05

mikakun


2 Answers

To wait for all handlers to finish before executing some code, you should use jQuery's deferred API. You can do something like this:

$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
  // only executes after both ajax calls have completed
});

Further, jQuery's trigger allows you to pass extra params. Pass in a function which will be the callback function.

Your final code should look something like this:

$doc.trigger('beforedo', function() {
  // anything here only executes after the event has been handled
});


$doc.on('beforedo',function(e, callback) {
   //do before addon stuff
  $.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
      // at this point, all your AJAX calls have completed
      // call the callback function
      callback();
  });
}

If need be, when you call callback() you can even pass in any result you might need to pass as an argument. Accordingly, change the function signature in your .trigger() call too.

Expanding on the comment I left below, you can have a common loader function if you like:

$.when(load("page1"), load("page2")).done(function(){
 // ...
});

function load(file) {
  // return the $.ajax promise
  return $.ajax(file);
}

See:

jQuery deferred
jQuery.when()
jQuery.trigger

like image 147
xbonez Avatar answered Oct 29 '22 23:10

xbonez


Check out my fiddle here. Your idea of counting finished requests is good, it's just a matter of structuring code. Knowing that you need decoupled "modules/addons" this is the way I'd go:

var $doc = $(document);

function Main() {
    var handlersCount = 0;
    var completedHandlers = 0;

    function afterDostg() {
        console.log('after addon stuff');
    }

    this.dostg = function() {
        $doc.trigger('beforeDo');
    };

    this.addHandler = function() {
        handlersCount++;
    };

    this.handleCompleted = function() {
        completedHandlers++;

        if(completedHandlers === handlersCount) {
            completedHandlers = 0;
            afterDostg();
        }
    }
}

function Addon1(main) {
    main.addHandler();

    $doc.on('beforeDo', function(e) {
        console.log('addon1 stuff');
        main.handleCompleted();
    });
}

function Addon2(main) {
    main.addHandler();

    $doc.on('beforeDo', function(e) {
        setTimeout(
            function() {
                console.log('addon2 stuff');
                main.handleCompleted();
            },
            1000
        );
    });
}

var main = new Main();
var addon1 = new Addon1(main);
var addon2 = new Addon2(main);

main.dostg();

Using this method, addons can have anything in them, they just have to notify "Main" when they finish whatever they need to do.

If I were you I'd go even further and extract the whole "handlers" code in "Main" in a separate class instantiated as a public property in "Main" with afterDostg as a parameter. That way you don't polute app code with meta stuff like this.

like image 25
Sergiu Paraschiv Avatar answered Oct 30 '22 01:10

Sergiu Paraschiv