2
\$\begingroup\$

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)));
    };
  };
});
\$\endgroup\$
2
\$\begingroup\$

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

\$\endgroup\$
  • \$\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 Jun 3 '15 at 6:55
  • \$\begingroup\$ Ok, and there must a reason you need to wait for listeners to finish? \$\endgroup\$ – Mikko Viitala Jun 8 '15 at 19:03
  • \$\begingroup\$ Yes - user interactions and ajax requests \$\endgroup\$ – jantimon Jun 8 '15 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 Jun 17 '15 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 Jun 18 '15 at 8:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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