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)));
};
};
});
1 Answer 1
Idea seems nice but for me it's quite hard to follow. Don't get me wrong, it might just be me!
But could you achieve the same functionality A proceeds when B has done it's thing
just by passing a callback function in your broadcasted/emitted event?
angular.module('moduleA', ['moduleB'])
.controller('OneCtrl', function($scope, $rootScope) {
// broadcast event
$scope.send = function() {
console.log('broadcast event');
$rootScope.$broadcast('event:my-event', callback);
};
// event callback
var callback = function(result) {
console.log('callback', result);
};
});
angular.module('moduleB',[])
.controller('TheOtherCtrl', function($scope, $q, $timeout) {
// receive event
$scope.$on('event:my-event', function(event, callback) {
console.log('received', event.name);
// execute callback with process result
process(true).then(callback, callback);
});
// some async processing
function process(success) {
return $timeout(function() {
return success ? $q.when(true) : $q.reject(false);
}, 800);
}
});
broadcast event
received event:my-event
callback true
-
\$\begingroup\$ Hey - thanks for the feedback - The reason why I chose the approach was that I don't know how many modules are listening on the event. \$\endgroup\$jantimon– jantimon2015年06月03日 06:55:18 +00:00Commented Jun 3, 2015 at 6:55
-
\$\begingroup\$ Ok, and there must a reason you need to wait for listeners to finish? \$\endgroup\$Mikko Viitala– Mikko Viitala2015年06月08日 19:03:15 +00:00Commented Jun 8, 2015 at 19:03
-
\$\begingroup\$ Yes - user interactions and ajax requests \$\endgroup\$jantimon– jantimon2015年06月08日 19:12:32 +00:00Commented Jun 8, 2015 at 19:12
-
\$\begingroup\$ This still bothers me. If I think e.g. conventional C# application and some class exposes event(s), then I can't think any case where I have cared about listeners and what it is those listeners are doing. \$\endgroup\$Mikko Viitala– Mikko Viitala2015年06月17日 13:05:51 +00:00Commented Jun 17, 2015 at 13:05
-
\$\begingroup\$ Well in Javascript has a lot of asynchronous code and you are able to decide in a listener function wether the event bubbles, executes the default behaviour and so on. \$\endgroup\$jantimon– jantimon2015年06月18日 08:48:48 +00:00Commented Jun 18, 2015 at 8:48