Node.js + AngularJS + socket.io:连接状态并手动断开 [英] Node.js + AngularJS + socket.io: connect state and manually disconnect

查看:19
本文介绍了Node.js + AngularJS + socket.io:连接状态并手动断开的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I use nodejs with socket.io and angularjs on client. I picked up angular-socketio example from the Internet and added disconnect method to It.

套接字服务:

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

}]);

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

But when I try to disconnect use this method I catch 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…

推荐答案

[更新]

$$phase 是 Angular 的一个内部私有变量,因此你不应该真正依赖它来做这样的事情.伊戈尔在另一个答案中描述了一些处理这个问题的建议,应该改用它(我听说他对 Angular 了解一两件事.;)

$$phase is an internal, private variable to Angular, and thus you should not really depend on it for things like this. Igor describes, in another answer, some suggestions for handling this which should be used instead (I hear he knows a thing or two about Angular. ;)

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

When models change and events fire from within the Angular framework, Angular can do dirty tracking as necessary and update any necessary views. When you want to interact with code outside of Angular, you have to wrap the necessary function calls in the $apply method of a scope, so that Angular knows something is happening. That's why the code reads

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

等等.它告诉 Angular,把这段通常不会触发 Angular 视图更新的代码,像它应该的那样对待它."

and so forth. It's telling Angular, "take this code that normally wouldn't trigger Angular view updates, and treat it like it should."

问题是当您已经在 $apply 调用中时调用 $apply.例如,下面的代码会抛出一个 $apply already in progress 错误:

The problem is when you call $apply when you're already in an $apply call. For example, the following would throw an $apply already in progress error:

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

根据您的堆栈跟踪,对 emit(已经使用了 $apply)的调用似乎触发了对 on 的调用(它也使用 $apply).为了解决这个问题,我们只需要在 $apply 尚未进行时调用 $apply.幸运的是,作用域中有一个名为 $$phase 的属性,可以告诉我们是否正在进行脏检查.

Based on your stack trace, it looks like some call to emit (which already uses $apply) triggered a call to on (which also uses $apply). To fix this problem, we need to only call $apply if an $apply is not already in progress. Thankfully, there is a property on the scope called $$phase that can tell us if a dirty check is in progress.

我们可以很容易地构建一个函数,它接受一个作用域和一个要运行的函数,然后只有在没有正在进行的情况下才使用 $apply 运行该函数:

We can easily build a function that takes a scope and a function to run, and then runs the function with $apply only if one isn't already in progress:

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...);

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

For example, to modify the code you have above,

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

  }]);

这篇关于Node.js + AngularJS + socket.io:连接状态并手动断开的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆