在JavaScript中同步基于事件的插件调用

时间:2011-06-15 22:05:57

标签: javascript javascript-events synchronization

我正在编写一些与浏览器插件交互的JavaScript(FF中的附加组件,IE中的ActiveX)。某些调用是异步的,并在完成时触发事件。在某些情况下,我需要将呼叫链接在一起,但需要在启动第二个呼叫之前等待第一个呼叫完成。例如:

连接设备的呼叫需要先检查设备是否已连接,然后断开连接。所以电话会是:

pluginObject.disconnectDevice(deviceKey);
pluginObject.connectDevice(deviceKey, backingInfo);

只是直接调用它们会失败,因为在断开连接实际完成之前启动了连接,并且插件无法处理。

我已经设置了听众,我可以做类似的事情:

function handleConnectionChange(connectionState, /* ... */, doReconnect) {
    if (connectionState === state.disconnected && doReconnect) {
        pluginObject.connectDevice(deviceKey, backingInfo);
    }
}

但我实际上更喜欢一种更通用的方法 - 即使使用一组不同的链式事件也可以重复使用。。在这个特定的例子中这很简单,但即使这样也有点草率。我已经将重新连接信息逻辑添加到连接事件处理程序中,我可以很容易地看到条件链在这样做时失控。

是否有任何图书馆可以解决这种工作流程?我查看了jQuery的延迟内容,它有我想要的链接,但它似乎并不适合回调。 Sproutcore的StateCharts看起来很有趣,但似乎还没有能力重做已经异步的流程。

更新:解决方案 感谢Gustavo的一些指示,我已经采用了包装插件调用的方法来使它们使用jQuery Deferred对象:

var connectDevice = function(deviceKey, backingInfo) {
    var deferred = $.Deferred();
    var connectHandler = function(event) {
        // unregister connectHandler
        if (event.connectionState === ERROR) {
            showAlert("Error connecting device", event.message);
            deferred.reject();
        } else {
            showAlert("Device connected", event.message);
             deferred.resolve();
        }
    };

    // register connectHandler
    pluginObject.connectDevice(deviceKey, backingInfo);
    return deferred.promise();
};

如果我为断开连接创建一个类似的包装器,我现在可以链接方法调用(或不依赖于流程):

if (forceDisconnect) {
    disconnectDevice(deviceKey).done(connectDevice(deviceKey, backingType));
} else {
    connectDevice(deviceKey, backingType);
}

我也可以使用connectDevice启动链,或者向forceDisconnect流添加另一个函数等。

3 个答案:

答案 0 :(得分:1)

为什么你说Deferreds不适合回调?在我看来

pluginObject.disconnectDevice(deviceKey).then(function() { 
    pluginObject.connectDevice(deviceKey, backingInfo);
});

非常自然。

如果您无法更改插件代码,您仍然可以使用此方法,但当然您必须将插件包装在某种API适配器中。基本上,在包装器中对disconnectDevice的每次调用都会创建一个Deferred,将其注册到队列中并返回它。当您收到断开事件时,您resolve按顺序排除所有待处理的延期(请注意重新入场)。

您需要为要处理的插件触发的每个事件执行此操作。

延迟方法具有改进的代码可读性和可维护性的好处IMHO(通过事件监听器API)。

答案 1 :(得分:0)

您是否只能向disconnectDevice添加回调并将来电传递给connectDevice

pluginObject.disconnectDevice = function ( key, callback ) {
    // do stuff and then when disconnect complete...
    callback();
};

...然后当你想要附加一个设备......

pluginObject.disconnectDevice(deviceKey, function() {
    pluginObject.connectDevice(deviceKey, backingInfo);
});

答案 2 :(得分:0)

假设您使用名为onConnectionChange的简单属性设置处理程序,您可以编写一个新函数来代替回调。

// The new function, it takes a callback to let you know
// that disconnecting is done
pluginObject.disconnect = function (deviceKey, callback) {
     var me = this;
     me.onConnectionChange = function (connectionState) {
        if (connectionState === state.disconnected) {
             delete me.onConnectionChange;
             callback();
        }
}

// Now you can call
pluginObject.disconnect(deviceKey, function() {
    pluginObject.connectDevice(deviceKey, backingInfo);
});