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

Synchronizing event-based plugin calls in JavaScript

本文关键字:事件 插件 调用 JavaScript 同步 于事件      更新时间:2023-09-26

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

连接设备的呼叫需要首先检查设备是否已经连接,然后断开连接。因此,呼叫将是:

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流添加另一个功能,等等。

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

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

读起来很自然。

如果你不能更改插件代码,你仍然可以使用这种方法,但当然你必须将插件包装在某种API适配器中。基本上,包装器中对disconnectDevice的每次调用都会创建一个Deferred,将其注册到队列中并返回。当您收到断开连接事件时,您会按顺序resolve所有挂起的延迟(注意重新进入)。

你需要为你想要处理的插件触发的每个事件做这件事。

延迟方法的优点是改进了代码可读性和可维护性IMHO(优于事件侦听器API)。

难道不能给disconnectDevice添加一个回调并将调用传递给connectDevice吗?

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

然后当你想连接一个设备。。。

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

假设使用名为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);
});