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

2019-07-19 06:46发布

我用socket.io和的NodeJS上angularjs客户。 我从网上拾取角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
    };

  }]);

控制器:

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

}]);

但是,当我尝试断开使用这种方法我赶上错误:

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)

我不明白的地方挖...

Answer 1:

[更新]

$$phase是角内,私有变量,这样的话你真的不应该依赖于它的这样的事情。 伊戈尔介绍,在另一个答案,处理这个一些建议应改为使用(我听到他知道的事情或两个角左右。)


当模型改变和事件从角范围内火,角度可以做肮脏的跟踪是必要和更新必要的意见。 当你想要的角度以外的代码进行交互,你必须包装所需的函数调用在$apply一个范围的方法,使角知道有事情发生。 这就是为什么代码读取

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

等等。 它告诉角,“采取这种代码通常不会触发角视图更新,并且它应该像对待它。”

问题是,当你调用$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
    };

  }]);


Answer 2:

这个问题在这个核心(就像在大多数其他情况下),是由于on方法异步调用的大部分时间(好!),但也同步在某些情况下(坏!)。

当你调用socket.disconnect()从应用程序(从控制器,它生活在“角语境”中),它同步触发断开事件,然后传播到on ,其目的是打开边界进入角范围内的方法。 但既然你已经在角范围内,角抱怨你提到的错误。

由于这个问题是特定于断开呼叫的最佳选择这里是

  • 使用的setTimeout或$超时使断开异步(与invokeApply ARG设置为false),或
  • 保持一个内部标志,将告诉你,如果你是在断开阶段,在这种情况下,跳过$申请

示例代码:

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

  }]);


文章来源: Node.js + AngularJS + socket.io: connect state and manually disconnect