2011-06-15 55 views
1

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

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

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

只是称他们为直像这样因为一个连接启动之前断开是实际完成会失败,并且插件不能处理这个问题。

我已经有听众设置,我可以这样做:

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

但我真的喜欢一个更通用的方法 - 一个是可重复使用的即使有一组不同的链接事件。。在这个特殊的例子中这很简单,但即使这样也有点sl。。我已经为连接事件处理程序添加了重新连接信息逻辑,并且在完成此操作后,我可以轻松地看到有条件的链接失控。

有没有解决这种工作流的库?我看着jQuery的延期东西,它有我想要的链接,但它似乎不适合回调。 Sproutcore的StateCharts看起来很有趣,但似乎又没有能力重做已经异步的流程。

更新:解决方案 感谢来自古斯塔沃一些指点,我已经与捆绑的插件调用,使他们利用jQuery的递延对象的方法:

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,或添加其他功能到forceDisco nnect flow等

+0

你如何设置该监听器?它只是'pluginObject.handleConnectionChange = function(){}?' – 2011-06-15 23:03:46

+0

是的,或IE中的attachEvent – Jeff 2011-06-15 23:42:50

+0

然后我的答案应该做的工作! – 2011-06-15 23:52:45

回答

1

为什么这么说Deferreds不适合与回调?在我看来,

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

看起来很自然。

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

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

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

+0

这是我正在寻找的东西。不知道为什么我无法绕过昨天放置resolve()/ reject()的头。这是一个额外的工作,可以包装所有的方法,但它确实提供了一个很好的通用方法来链接事件(或不)。用类似于我最后的方法更新问题。 – Jeff 2011-06-16 19:41:06

0

难道你不能只是添加一个回调到disconnectDevice并传递给connectDevice

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

...然后当你想连接的设备...

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

disconnectDevice方法由插件定义。我只能传递它期望的值,不包括回调。 – Jeff 2011-06-15 22:57:47

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); 
});