Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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)));
    };
  };
});
share|improve this question

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

share|improve this answer
    
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. – jantimon Jun 3 '15 at 6:55
    
Ok, and there must a reason you need to wait for listeners to finish? – Mikko Viitala Jun 8 '15 at 19:03
    
Yes - user interactions and ajax requests – jantimon Jun 8 '15 at 19:12
    
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. – Mikko Viitala Jun 17 '15 at 13:05
    
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. – jantimon Jun 18 '15 at 8:48

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.