我们有一个使用SignalR访问API的应用程序。在启动/加载页面时,有两件事会异步发生
Login
的API调用。如果在Login
呼叫完成之前完成SignalR连接,则没有问题。但是如果首先调用API,我们需要推迟调用函数,直到SignalR连接准备就绪。
我解决这个问题的方法是在包含SignalR逻辑的服务中:
return {
init: init,
login: function (username, password) {
waitForSignalRToHaveStarted().then(function () {
$q.when(self.proxy.invoke('Login', username, password));
});
},
...
在服务的主要部分中包含以下内容:
function init() {
self.connection.start().done(function () {
notifyStartWaiters();
$log.log('WebSocket Started: ' + self.connection.id);
});
}
var isStarted = false;
var waitForSignalRToHaveStartedQueue = [];
function waitForSignalRToHaveStarted() {
if (isStarted) {
// Return an empty promise that will resolve immideately
return $q.when();
}
// Create and store a new promise that will be resolved later
var deferred = $q.defer();
waitForSignalRToHaveStartedQueue.push(deferred);
return deferred.promise;
}
function notifyStartWaiters() {
isStarted = true;
// http://stackoverflow.com/a/25202605/23118
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
var initialPromise = $q.when();
waitForSignalRToHaveStartedQueue.reduce(function (prev, data) {
return prev.then(function () {
return data.resolve();
});
}, initialPromise).then(function () {
waitForSignalRToHaveStartedQueue = [];
});
}
这是有效的。但它感觉不太正确,现在我应该为其他API调用创建一个相应的waitForLoginToBeSuccessful,我真的不希望通过复制和修改waitForSignalRToHaveStarted和notifyStartWaiters来获得大量重复代码。
那么在其他方面可以处理这个问题呢?
答案 0 :(得分:1)
你是对的,这感觉不太对劲。您不必为完成事件实现自己的事件发射器 - 承诺已经为您执行此操作。现在,根据您可能能够完全避免延迟的行为。
var p = null;
function readyConnection(){
if(p) return p; // already started connection
return p = self.connection.start(); // start connection and init
}
function init(){
readyConnection().then(function(){ console.log("..."); });
}
function waitForSignalRToHaveStarted(){
return readyConnection(); // just ready the connection
}
init
:这比较棘手,您必须使用 a 延迟,但可以避免重新实现事件发射器逻辑:
var d = $q.defer();
// we have no choice since `connection.start` doesn't offer this functionality
// so we resolve the deferred. This style of programming is very uncommon
// the first approach is simpler.
function init(){
self.connection.start().then(function(c){ d.resolve(c); });
}
function waitForSignalRToHaveStarted(){
return d.promise; // we just return the promise, you chain to it already
}
另一方面 - 您在waitForSi...
实现中的逻辑与.then
内的(天真)承诺实现非常接近。我建议您阅读this good write。