Javascript while循环中的异步函数

Javascript while循环中的异步函数,javascript,asynchronous,callback,theory,synchronous,Javascript,Asynchronous,Callback,Theory,Synchronous,我有一个问题,关于如何在while循环中执行异步任务,直到满足某些条件。这更多的是一个理论问题,但我可以看出在某些情况下这可能是一个问题 我将尝试在一个示例中演示这个问题(我在这里使用JavaScript,但您可以使用任何语言): 我可以有一个设备,我想保留我的应用程序,直到该设备达到特定状态。如果我可以使用synchronouse方法获取设备状态,则代码可能如下所示: device.registerStateChangeCallback("type of state interested in

我有一个问题,关于如何在while循环中执行异步任务,直到满足某些条件。这更多的是一个理论问题,但我可以看出在某些情况下这可能是一个问题

我将尝试在一个示例中演示这个问题(我在这里使用JavaScript,但您可以使用任何语言):

我可以有一个设备,我想保留我的应用程序,直到该设备达到特定状态。如果我可以使用synchronouse方法获取设备状态,则代码可能如下所示:

device.registerStateChangeCallback("type of state interested in", fn);
device.registerStateChangeCallback("syncState", function(newState) {
     // caller puts code here that wants to do something when 
     // the syncState has changed
});
//保持,直到达到所需状态
var状态=假;
而(!state){
state=device.getStateSync();
}
//[…]继续该计划
我现在的问题是:当我从设备获得的只是一个异步getState函数时,如何转换此代码?无论调用的执行时间有多长,代码都应该工作,并且应该记住,我使用的是有限的内存和堆栈大小

/[…]这将在循环中调用异步函数并使程序崩溃
而(!state){
//[…]什么
getStateAncy(函数(newState){
状态=新闻状态;
});
//[…]还有别的吗
}
我发现的这篇文章有一个递归解决方案()。虽然这是一个很好的解决方案,但如果太频繁地调用循环,在某些时候它会遇到堆栈大小问题

现在我有种直觉,可能没有解决办法。你知道怎么做吗?或者你知道如何证明没有办法做到这一点吗?可以自由地包含更复杂的概念,如线程、承诺或未来

请记住,这是一个理论问题,示例是针对我无法更改正在使用的框架(或设备)的情况

感谢您的回复和建议


佩德罗这确实是一个非常理论化的问题。有一个异步API来获取当前状态是非常奇怪的。您很可能会:

  • 提供当前状态的同步方法
  • 或者是一个异步方法,当状态改变时,它会回叫您。在这种情况下,不需要循环,只需等待回调

但是如果您确实有这样一个API的特定示例,请告诉我们……

您不能这样做。原因是Javascript是单线程的。当
While
循环正在运行时,其他任何任务都不会运行,因此,任何需要更新设备状态的异步任务都不会有机会运行。Javascript中的异步任务由主事件处理程序循环运行。这意味着异步操作将不会运行,直到您从脚本返回到事件处理程序,或者脚本执行对事件处理程序的调用(仅当脚本使用
confirm
prompt
等待用户输入时才会运行),除非实际更改条件的代码在该循环中,或者是循环中调用的某个函数的副作用,否则不能循环等待条件更改。这是因为javascript是单线程的(除了这里不考虑的webworkers),所以只要循环在javascript中循环,就没有其他代码可以运行,因此没有其他代码可以更改您的条件变量。您将拥有一个无限循环,因为循环等待着一些永远不会改变的东西。最终,浏览器会抱怨您运行了无响应的代码,并将其关闭

因此,javascript中没有不确定或长等待循环。可以循环一秒钟左右,让一段时间过去,但这很少有用、高效或是编写JS代码的最佳方式

相反,您必须在条件更改时触发事件或回调,并且感兴趣的代码可以订阅该事件或注册其回调。或者,您必须在计时器上进行轮询,以查看条件发生了什么变化(第一个选项是首选选项)


如果您正在设计一个API,希望能够允许一些调用代码知道状态何时更改,通常您会实现回调或承诺。回调方法可能如下所示:

device.registerStateChangeCallback("type of state interested in", fn);
device.registerStateChangeCallback("syncState", function(newState) {
     // caller puts code here that wants to do something when 
     // the syncState has changed
});
然后,每当指定的状态更改为新值时,API将调用传入的回调函数。这是一次性通知还是每次状态更改时都会发生,直到取消注册回调为止,这取决于API

因此,与其让调用者在忙循环中等待状态更改,调用者使用回调编写异步代码(这就是javascript处理此类内容的方式),该回调将在稍后状态更改时调用。例如,调用方的代码可能如下所示:

device.registerStateChangeCallback("type of state interested in", fn);
device.registerStateChangeCallback("syncState", function(newState) {
     // caller puts code here that wants to do something when 
     // the syncState has changed
});

如果通知仅用于一次,那么您也可以使用承诺,API只返回一个承诺,该承诺在syncState更改时解析:

device.registerStateChange("syncState").then(function(newState) {
     // caller puts code here that wants to do something when 
     // the syncState has changed
});

承诺的缺点是它们纯粹是一次性的(仅一个通知),因此如果您想要多个通知,那么最好使用回调。承诺优于回调的优点在于,它们提供了许多功能,用于将承诺与其他事件同步(如排序、等待一系列事件全部完成、协调多个异步事件等)而且它们提供了更好的异步错误处理。

此函数使用setTimeout,因此回调返回时不会递归。要求您的环境实现setTimeout机制,因为这不是JavaScript的一部分

var callback = function callback(result){
   if(result == "some condition"){
       //done
   } else {
       setTimeout(device.getStateAsync(callback), 10);
   }
}

device.getStateAsync(callback);

我知道这已经很晚了,但是es6提供了一个更好的方法。 解决方案在于使用
生成器
+
承诺

看看这个存储库,我必须在其中创建Twitter线程,前面的tweet必须回复前面的tweet并使用,这是由fet完成的