我在客户端上将nodejs与socket.io和angularjs一起使用。我从互联网上获取了angular- socketio示例,并向其中添加了disconnect方法。
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)
而且我不知道在哪里挖……
[更新]
$$phase是Angular的内部私有变量,因此您不应该真正依赖于此类事情。伊戈尔在另一个答案中描述了一些处理此问题的建议,应该改用一些建议(我听说他对Angular知道一两件事。)
$$phase
当模型发生变化并且事件从Angular框架内触发时,Angular可以根据需要进行脏跟踪并更新任何必要的视图。当您想与Angular 之外 的代码进行交互时,您必须将必要的函数调用包装在$apply作用域的方法中,以便Angular知道正在发生的事情。这就是代码读取的原因
$apply
$rootScope.$apply(function () { callback.apply(socket, args); });
等等。它告诉Angular,“采用通常不会触发Angular视图更新的代码,并按应有的方式对待它。”
问题是当您$apply已经在通话中时$apply拨打电话。例如,以下内容将引发$apply already in progress错误:
$apply already in progress
$rootScope.$apply(function() { $rootScope.$apply(function() { // some stuff }); });
根据您的堆栈跟踪,看起来某些调用emit(已经使用$apply)触发了调用on(也使用$apply)。为了解决这个问题,我们只需要调用$apply如果$apply不是已在进行中。值得庆幸的是,作用域上有一个属性$$phase,可以告诉我们是否正在进行脏检查。
emit
on
我们可以轻松地构建一个需要作用域和功能才能运行的功能,然后$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 }; }]);