Node.js + AngularJS + socket.io:连接状态并手动断开连接

Max*_*ach 5 node.js socket.io angularjs

我在客户端上使用带有socket.io和angularjs的nodejs.我从互联网上获取了angular-socketio示例并添加了disconnect方法.

套接字服务:

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
    };

  }]);
Run Code Online (Sandbox Code Playgroud)

控制器:

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) { ... }

}]);
Run Code Online (Sandbox Code Playgroud)

但当我尝试断开使用此方法时,我发现错误:

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)
Run Code Online (Sandbox Code Playgroud)

我不明白在哪里挖...

Mic*_*ley 8

[更新]

$$phase是Angular的一个内部私有变量,因此你不应该真的依赖它来做这样的事情.在另一个答案中,Igor描述了一些处理这个问题的建议,应该使用它(我听说他知道关于Angular的一两件事.)


当模型发生变化并且事件从Angular框架内触发时,Angular可以根据需要进行脏跟踪并更新任何必要的视图.当您想要与Angular 之外的代码进行交互时,您必须在$apply范围的方法中包含必要的函数调用,以便Angular知道正在发生的事情.这就是代码读取的原因

$rootScope.$apply(function () {
  callback.apply(socket, args);
});
Run Code Online (Sandbox Code Playgroud)

等等.它告诉Angular,"采用通常不会触发Angular视图更新的代码,并按照它应该的方式对待它."

问题是当你$apply已经在通话时$apply打电话.例如,以下内容将引发$apply already in progress错误:

$rootScope.$apply(function() {
  $rootScope.$apply(function() {
    // some stuff
  });
});
Run Code Online (Sandbox Code Playgroud)

根据您的堆栈跟踪,它看起来像一些调用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
  }
};
Run Code Online (Sandbox Code Playgroud)

现在我们可以替换来电

$rootScope.$apply(function...);
Run Code Online (Sandbox Code Playgroud)

safeApply($rootScope, function...);
Run Code Online (Sandbox Code Playgroud)

例如,要修改上面的代码,

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
    };

  }]);
Run Code Online (Sandbox Code Playgroud)