我有一个关于如何在while循环中执行异步任务的问题,直到满足某些条件。这更像是一个理论问题,但我可以看到在某些情况下这可能是一个问题。
我尝试在一个示例中演示问题(我在这里使用JavaScript,但您可以使用任何语言):
我可以拥有一台设备,我希望保留我的应用程序,直到该设备达到特定状态。如果我可以使用设备状态进行getrieve的方法是同步,则代码可能如下所示:
// Hold until the desired state is reached
var state = false;
while (!state) {
state = device.getStateSync();
}
// [...] continue the program
我现在的问题是:当我从设备获取的是异步getState函数时,如何转换此代码?无论调用的执行时间有多长,代码都应该工作,并且应该记住我正在处理有限的内存和堆栈大小。
// [...] This would call the async function in a loop and crash the program
while (!state) {
// [...] something
device.getStateAsync(function(newState) {
state = newState;
});
// [...] something else
}
我找到的这篇帖子有一个递归解决方案(http://blog.victorquinn.com/javascript-promise-while-loop)。虽然这是一个很好的解决方案,但如果过于频繁地调用循环,它在某些时候会遇到堆栈大小的问题。
现在我有直觉,可能没有解决方案。你知道有什么办法吗?或者你知道如何证明没有办法吗?随意包含更复杂的概念,如线程,承诺或期货。
请记住,这是一个理论问题,例如我无法改变我正在使用的框架(或设备)。
感谢您的每一个回应和想法!
佩德罗
答案 0 :(得分:4)
在javascript中,除非实际更改条件的代码位于循环内部或循环中调用的某个函数的副作用,否则无法循环等待条件更改。这是因为javascript是单线程的(除了这里没有考虑的webworkers),只要循环在javascript中循环,没有其他代码可以运行,所以没有其他代码可以改变你的条件变量。当循环等待永远不会改变的东西时,你将只有一个无限循环。最终,浏览器会抱怨您没有响应代码并将其关闭。
因此,javascript中没有不确定或长等待循环。可以循环一秒左右只是为了让一些时间过去,但这很少有用,有效或者是编写JS代码的最佳方式。
相反,您必须在条件更改时触发事件或回调,并且感兴趣的代码可以订阅该事件或注册其回调。或者,您必须轮询计时器以查看条件已更改(第一个选项是首选选项)。
如果您正在设计一个API,希望能够允许某些调用代码知道状态何时发生变化,那么通常会实现回调或承诺。回调方法可能如下所示:
device.registerStateChangeCallback("type of state interested in", fn);
然后,只要指定的状态更改为新值,API就会调用传入的回调函数。它取决于API,无论是一次性通知,还是每次状态发生变化时都会发生,直到取消注册回调为止。
因此,调用者不会让调用者在繁忙的循环中等待状态更改,而是调用异步代码(这就是javascript对这种东西起作用的方式),并且会在稍后状态更改时调用回调函数。例如,来电者的代码可能如下所示:
device.registerStateChangeCallback("syncState", function(newState) {
// caller puts code here that wants to do something when
// the syncState has changed
});
如果通知只是一次性,那么您也可以使用promises,API只会返回一个在syncState更改时解析的promise:
device.registerStateChange("syncState").then(function(newState) {
// caller puts code here that wants to do something when
// the syncState has changed
});
承诺的缺点是它们纯粹是单一用途(仅一个通知),因此如果您想要多个通知,那么最好使用回调。承诺优于回调的优势在于它们提供了许多功能,可以将它们与其他事件同步(例如排序,等待一系列事件全部完成,协调多个异步事件等等)并且它们提供更好的异步错误处理。
答案 1 :(得分:2)
你不能这样做。原因是Javascript是单线程的。当while
循环正在运行时,没有其他任何东西运行,因此无论预期更新什么异步任务,设备状态都将永远无法运行。 Javascript中的异步任务由主事件处理程序循环运行。这意味着在您从脚本返回到事件处理程序之前,异步操作不会运行,或者脚本会对事件处理程序执行调用(仅当脚本等待用户输入confirm
时才会执行此操作或prompt
)。
答案 2 :(得分:0)
这确实是一个非常理论化的问题。拥有一个异步API来获取当前状态会很奇怪。你很可能会有:
但是如果您确实有这样一个API的具体示例,请告诉我们......
答案 3 :(得分:0)
此函数使用setTimeout,因此回调不返回。要求是您的环境实现了setTimeout机制,因为这不是JavaScript的一部分。
var callback = function callback(result){
if(result == "some condition"){
//done
} else {
setTimeout(device.getStateAsync(callback), 10);
}
}
device.getStateAsync(callback);
答案 4 :(得分:0)
我知道这是迟到的,但es6提供了一个更好的方法来做到这一点。
解决方案在于使用Generators
+ Promises
。
看一下我必须创建Twitter线程的存储库,其中前面的推文必须回复之前和使用,这是通过获取当前推文的ID并使用它来发布下一条推文。