我正在编写一些与浏览器插件交互的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流添加另一个函数等。
答案 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);
});