【问题标题】:Synchronizing event-based plugin calls in JavaScript在 JavaScript 中同步基于事件的插件调用
【发布时间】:2011-06-15 22:05:57
【问题描述】:

我正在编写一些与浏览器插件交互的 JavaScript(Firefox 中的插件,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流中添加另一个函数等等。

【问题讨论】:

  • 你如何设置那个监听器?只是pluginObject.handleConnectionChange = function() {}?
  • 那我的回答应该可以了!

标签: javascript synchronization dom-events


【解决方案1】:

为什么你说延迟不适合回调?在我看来,

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

读起来很自然。

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

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

延迟方法的好处是提高了代码的可读性和可维护性恕我直言(通过事件侦听器 API)。

【讨论】:

  • 这正是我所寻找的。不知道为什么我昨天无法解决 resolve()/reject() 的位置问题。包装所有方法需要一些额外的工作,但它确实提供了一种很好的通用方式来链接事件(或不链接)。使用类似于我的最终方法的内容更新问题。
【解决方案2】:

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

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

...那么当您想附加设备时...

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

【讨论】:

  • disconnectDevice 方法由插件定义。我只能传入它所期望的值,不包括回调。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-13
    • 2023-03-24
    • 2023-04-03
    • 2023-03-03
    相关资源
    最近更新 更多