如何等待过去可能发生过的事情?

时间:2014-10-23 12:54:30

标签: angularjs promise

我们有一个使用SignalR访问API的应用程序。在启动/加载页面时,有两件事会异步发生

  1. 启动SignalR连接。这将采取不同的变化 完成之前的时间(或者如果API,可能根本没有) 服务器不可用。)
  2. 渲染包含的html模板代码 某些指令会触发对Login的API调用。
  3. 如果在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来获得大量重复代码。

    那么在其他方面可以处理这个问题呢?

1 个答案:

答案 0 :(得分:1)

你是对的,这感觉不太对劲。您不必为完成事件实现自己的事件发射器 - 承诺已经为您执行此操作。现在,根据您可能能够完全避免延迟的行为。

如果您希望操作初始化尚未初始化SignalR:

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