Design pattern for waiting on asynchronous event h

2019-05-31 00:25发布

问题:

There are places in my application where an object should emit an event and should delay execution untill all event handlers have completed work. The event handlers might be performing an asynchronous operation (e.g. writing to database).

Application.on("login", function(evt) {
    // Asynchronous operation (e.g. update info in database)
});

Application.trigger("login").then(function() {
    // Execute stuff after all (async) event handlers are done
});

I've looked around and haven't found any established design patterns for this. I've got an implementation I'm happy with, but I'm curious if anyone has solved this differently?

回答1:

My solution is to add a promises[] property to the evt argument. Asynchronous event callbacks simply add a promise to this array and the triggerAsync function returns a promise that wait on all the promises to settle.

http://jsfiddle.net/nonplus/9aCC4/

Implementation of the triggerAsync method (using Backbone events and jQuery promises, but should work for other stacks, too):

// Trigger an event and return promise that waits on
// event handler promises
function triggerAsync(emitter, name, evt) {
    evt || (evt={});
    evt.promises = [];
    emitter.trigger(name, evt);
    return $.when.apply(null, evt.promises);
}

Usage of triggerAsync:

triggerAsync(obj, "myEvent").then(function() {
    // Code executed after event handlers are done
});

Implementation of an async event handler:

// Async handler doing an ajax request
obj.on("myEvent", function (evt) {
    evt.promises.push($.ajax(...));
});

// Async handler doing generic async operation
obj.on("myEvent", function (evt) {
    var dfd = $.Deferred();
    evt.promises.push(dfd.promise());

    // Async operation
    // Eventually calls dfd.resolve() or dfd.reject()
});