我用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)
我不明白的地方挖...
[更新]
$$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
};
}]);
这个问题在这个核心(就像在大多数其他情况下),是由于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
};
}]);