簡體   English   中英

while循環中的異步函數

[英]Asynchronous function in a while-loop

我有一個問題,關於如何在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 )。 雖然這是一個很好的解決方案,但在某些時候,如果過於頻繁地調用循環,它將遇到堆棧大小的問題。

現在我有直覺,可能沒有解決方案。 你知道有什么辦法嗎? 或者你知道如何證明沒有辦法嗎? 隨意包含更復雜的概念,如線程,承諾或期貨。

請記住,這是一個理論問題,例子是我無法改變我正在使用的框架(或設備)的情況。

感謝您的每一個回應和想法!

佩德羅

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

承諾的缺點是它們純粹是單一用途(僅一個通知),所以如果你想要多個通知,那么最好使用回調。 承諾優於回調的優勢在於它們提供了許多功能,可以將它們與其他事件同步(例如排序,等待一系列事件全部完成,協調多個異步事件等等)並且它們提供更好的異步錯誤處理。

你不能這樣做。 原因是Javascript是單線程的。 while循環正在運行時,沒有其他任何東西運行,因此無論預期更新任何異步任務,設備狀態都將永遠無法運行。 Javascript中的異步任務由主事件處理程序循環運行。 這意味着在您從腳本返回到事件處理程序之前,異步操作將不會運行,或者腳本會對事件處理程序執行調用(這僅在腳本通過confirmprompt等待用戶輸入時才會發生)。

這確實是一個非常理論化的問題。 擁有一個異步API來獲取當前狀態會很奇怪。 你很可能會有:

  • 一種同步方法,可以為您提供當前狀態;
  • 或者是一種異步方法,可以在狀態發生變化時回復您。 在這種情況下,無需循環,只需等待回調即可。

但是,如果你確實有這樣一個API的具體例子,請告訴我們......

此函數使用setTimeout,因此回調返回而不遞歸。 要求是您的環境實現了setTimeout機制,因為這不是JavaScript的一部分。

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

device.getStateAsync(callback);

我知道這是遲到的,但es6提供了一個更好的方法來做到這一點。 解決方案在於使用Generators + Promises

看看這個我必須創建Twitter線程的存儲庫,其中前面的推文必須回復之前和使用,這是通過獲取當前推文的ID並使用它來發布下一條推文來完成的。

看這里=> https://github.com/Udokah/tweet-threader

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM