Node.js + AngularJS + socket.io: enable state and manually disable

I am using nodejs with socket.io and angularjs on the client. I took an example of angular-socketio from the Internet and added a method to it disconnect.

Socket Service:

angular.module('app')
  .factory('socket', ['$rootScope', function ($rootScope) {

    var socket = io.connect();

    return {
      on: function (eventName, callback) {
        socket.on(eventName, function () {  
          var args = arguments;
          $rootScope.$apply(function () {
            callback.apply(socket, args);
          });
        });
      },
      emit: function (eventName, data, callback) {
        socket.emit(eventName, data, function () {
          var args = arguments;
          $rootScope.$apply(function () {
            if (callback) {
              callback.apply(socket, args);
            }
          });
        })
      },
      disconnect: function () {
        socket.disconnect();
      },
      socket: socket
    };

  }]);

Controller:

angular.module('app')
  .controller('Controller', ['$scope', 'socket', function ($scope, socket) {

    socket.emit('register')

    socket.on('connect', function () {
        console.log('Socket connected');
    });

    socket.on('disconnect', function () {
        console.log('Socket disconnected');
    });

    socket.on('register', function (reginfo) {
        console.log('Register: %s, cname=%s', reginfo.ok, reginfo.cname);
        socket.disconnect(); // <-- this line throw Error
    });

    socket.on('last', updateSnapshot);

    socket.on('state', updateSnapshot);

    function updateSnapshot(snapshot) { ... }

}]);

But when I try to disable the use of this method, I caught an error:

Error: $apply already in progress
  at Error (<anonymous>)
  at beginPhase (http://localhost:4000/scripts/vendor/angular.js:8182:15)
  at Object.$get.Scope.$apply (http://localhost:4000/scripts/vendor/angular.js:7984:11)
  at SocketNamespace.on (http://localhost:4000/scripts/services/socket.js:10:32)
  at SocketNamespace.EventEmitter.emit [as $emit] (http://localhost:4000/socket.io/socket.io.js:633:15)
  at Socket.publish (http://localhost:4000/socket.io/socket.io.js:1593:19)
  at Socket.onDisconnect (http://localhost:4000/socket.io/socket.io.js:1970:14)
  at Socket.disconnect (http://localhost:4000/socket.io/socket.io.js:1836:12)
  at SocketNamespace.<anonymous> (http://localhost:4000/scripts/controllers/controller.js:38:34)
  at on (http://localhost:4000/scripts/services/socket.js:11:34)

And I don’t understand where to dig ...

+5
source share
2 answers

[Update]

$$phaseis an internal, private variable in Angular, and therefore you should not depend on it for such things. In another answer, Igor describes some suggestions for dealing with this that should be used instead (I heard that he knows something about Angular .;)


Angular, Angular . Angular, $apply , Angular , - .

$rootScope.$apply(function () {
  callback.apply(socket, args);
});

.. Angular, " , Angular , ".

, $apply, $apply. , $apply already in progress:

$rootScope.$apply(function() {
  $rootScope.$apply(function() {
    // some stuff
  });
});

, , - emit ( $apply) on ( $apply). , $apply, $apply . , $$phase , , .

, , $apply , :

var safeApply = function(scope, fn) {
  if (scope.$$phase) {
    fn(); // digest already in progress, just run the function
  } else {
    scope.$apply(fn); // no digest in progress, run the function with $apply
  }
};

$rootScope.$apply(function...);

safeApply($rootScope, function...);

, , ,

angular.module('app')
  .factory('socket', ['$rootScope', function ($rootScope) {

    var safeApply = function(scope, fn) {
      if (scope.$$phase) {
        fn(); // digest already in progress, just run the function
      } else {
        scope.$apply(fn); // no digest in progress, run with $apply
      }
    };

    var socket = io.connect();

    return {
      on: function (eventName, callback) {
        socket.on(eventName, function () {  
          var args = arguments;
          safeApply($rootScope, function () {
            callback.apply(socket, args);
          });
        });
      },
      emit: function (eventName, data, callback) {
        socket.emit(eventName, data, function () {
          var args = arguments;
          safeApply($rootScope, function () {
            if (callback) {
              callback.apply(socket, args);
            }
          });
        })
      },
      disconnect: function () {
        socket.disconnect();
      },
      socket: socket
    };

  }]);
+8

( ) , on (!), (!).

socket.disconnect() ( , angular), , on, angular. angular, angular .

,

  • , setTimeout $timeout ( invokeApply arg false)
  • , , , $apply

:

angular.module('app')
  .factory('socket', ['$rootScope', function ($rootScope, $timeout) {

    var socket = io.connect();

    return {
      on: function (eventName, callback) {
        socket.on(eventName, function () {  
          var args = arguments;
          $rootScope.$apply(function () {
            callback.apply(socket, args);
          });
        });
      },
      emit: function (eventName, data, callback) {
        socket.emit(eventName, data, function () {
          var args = arguments;
          $rootScope.$apply(function () {
            if (callback) {
              callback.apply(socket, args);
            }
          });
        })
      },
      disconnect: function () {
        $timeout(socket.disconnect, 0, false);
      },
      socket: socket
    };

  }]);

angular.module('app')
  .factory('socket', ['$rootScope', function ($rootScope) {

    var socket = io.connect(),
        disconnecting = false;

    return {
      on: function (eventName, callback) {
        socket.on(eventName, function () {  
          var args = arguments;
          if (!disconnecting) {
            $rootScope.$apply(function () {
              callback.apply(socket, args);
            });
          } else {
            callback.apply(socket, args);
          }
        });
      },
      emit: function (eventName, data, callback) {
        socket.emit(eventName, data, function () {
          var args = arguments;
          $rootScope.$apply(function () {
            if (callback) {
              callback.apply(socket, args);
            }
          });
        })
      },
      disconnect: function () {
        disconnecting = true;
        socket.disconnect();
      },
      socket: socket
    };

  }]);
+4

All Articles