Okay I have the following use case:
Module A
broadcasts an event.
Module B
listens to that event.
Maybe also Module C
listens to that event.
Now Module B
has to perform an asynchronous operation before Module A
may proceed with its code.
I solved this by creating an custom module $deferredEvents
which allows to queue up asynchronous tasks before proceeding.
Example: http://jsfiddle.net/68845rw4/
Module:
angular.module('deferredEvents', []).provider('$deferredEvent', function () {
this.$get = function ($q) {
/**
* @param type String - 'broadcast' | 'emit'
* @param $scope Scope
* @param eventName String - name of the event
*/
return function $deferredEvent(type, $scope, eventName) {
var queueHelper = {
queue: [],
promise: $q.when(),
/**
* Promise will be executed after all previous promisses passed
*/
appendThen: function appendThen () {
return this.queuePromise(this.promise.then.apply(this.promise, arguments));
},
/**
* Promise will be executed immediately
*/
queuePromise: function queuePromise (promise) {
if (promise === this.promise) {
throw new Error('Can\'t wait for own promise');
}
// Execute promise
promise = $q.when(promise);
// Add the promise result to the queue
this.promise = this.promise.then(function () {
return promise;
});
return this.promise;
}
};
var args = Array.prototype.slice.call(arguments, 3);
var emitArgs = [eventName, queueHelper].concat(args);
var event = $scope['$' + type].apply($scope, emitArgs);
return queueHelper.promise.then(function() {
return $q.all(queueHelper.queue);
}).then(function (results) {
var failed = results.some(function (val) {
return val === false;
});
return failed ? $q.reject(event) : event;
}, function () {
return $q.reject(event);
});
};
};
}).provider('$deferredEmit', function () {
this.$get = function ($deferredEvent) {
return function $deferredEmit($scope, eventName) {
return $deferredEvent.apply(this, ['emit', $scope, eventName].concat(Array.prototype.slice.call(arguments, 2)));
};
};
}).provider('$deferredBroadcast', function () {
this.$get = function ($deferredEvent) {
return function $deferredBroadcast($scope, eventName) {
return $deferredEvent.apply(this, ['broadcast', $scope, eventName].concat(Array.prototype.slice.call(arguments, 2)));
};
};
});