2012-07-11 374 views
1

簡介問題循環中調用異步函數

我需要,直到條件滿足調用一個循環內的異步函數。這個特定的函數發送一個POST請求到一個網站form.php,並執行一些操作與響應,這是一個JSON字符串表示一個對象與ID字段。所以,當該id爲null時,外部循環必須結束。該函數類似以下內容:

function asyncFunction(session) { 
    (new Request({ 
     url: form.php, 
     content: "sess=" + session, 
     onComplete: function (response) { 
      var response = response.json; 
      if (response.id) { 
       doStaff(response.msg); 
      } else { 
       // Break loop 
      } 
     } 
    })).get(); 
} 

注意:雖然我已經找到了實現一個用於Firefox的附加問題,我認爲這是一個普遍的JavaScript的問題。


實施循環遞歸

我已經試過落實遞歸性的循環,但它沒有工作,我不知道這是正確的方式。

... 
    if (response.id) { 
     doStaff(response.msg); 
     asyncFunction(session); 
    } else { 
     // Break loop 
    } 
... 

使用jsdeferred

我也試圖與jsdeferred庫:

Deferred.define(this); 
//Instantiate a new deferred object 
var deferred = new Deferred(); 

// Main loop: stops when we receive the exception 
Deferred.loop(1000, function() { 
     asyncFunction(session, deferred); 
     return deferred; 
}). 
error(function() { 
    console.log("Loop finished!"); 
}); 

,然後調用:

... 
    if (response.id) { 
     doStaff(response.msg); 
     d.call(); 
    } else { 
     d.fail(); 
    } 
... 

我實現序列化,但它開始重複以前要求每次迭代。例如,如果它是第三次稱爲asyncFunction,它會調用與迭代1和2中的相應參數相同的函數。

回答

2

您的問題並不完全清楚,但基本架構必須是異步操作的完成事件處理程序必須決定是重試還是簡單返回。如果操作的結果需要另一次嘗試,則處理程序應調用父函數。如果不是,那麼通過簡單退出循環就會結束。

你不能在JavaScript中用類似於簡單的「循環」結構的東西編寫這樣的東西,因爲操作是異步。操作的結果不會以允許循環機制對結果進行測試的方式發生;在結果可用之前,循環可能會運行數千次迭代。換句話說,你不會「等待」使用代碼的異步操作。您無所作爲等待,並允許註冊的事件處理程序在結果準備就緒時接管。

+0

我明白你的意思了,但是,我可以使用其他什麼策略?必須有一些模式來執行我想要做的事情,對吧? – synack 2012-07-11 14:17:33

+0

我剛纔描述了這個策略。代碼可以採用一些不同的形式,但基本上你沒有選擇。 @Kayo給出了一個實際代碼的例子,但是你會看到在「完成」函數中(這將通過完成異步過程來觸發),調用「newAsyncRequest」函數。 – Pointy 2012-07-11 14:43:32

1

如果我正確理解你想要做什麼,推遲是一個好方法。下面是一個使用jQuery的示例,其中內置了延遲功能(jQuery.Deferred

超時用於模擬http請求。當每個超時完成時(或者http請求完成),返回一個隨機數字,它等於你的http請求的結果。

根據請求的結果,您可以決定是否需要另一個http請求或要停止。

試試下面的代碼片段。包括jQuery文件和片段。它將打印值保存在控制檯中,並在達到零點後停止。

這可能需要一段時間才能理解,但很有用。

$(function() { 

    var MAXNUM = 9; 

    function newAsyncRequest() { 
     var def = $.Deferred(function(defObject) { 
      setTimeout(function() { 
       defObject.resolve(Math.floor(Math.random() * (MAXNUM+1))); 
      }, 1000); 
     }); 

     def.done(function(val) { 
      if (val !== 0) 
       newAsyncRequest(); 
      console.log(val); 
     }); 

    }; 

    newAsyncRequest(); 
}); 

建議後更新從@canuckistani

@canuckistani is correct in his answer。對於這個問題,解決方案更簡單。如果不使用延遲,上面的代碼片段變成以下內容。對不起,我帶領你採取更嚴厲的解決方案。

$(function() { 

    var MAXNUM = 9; 

    function newAsyncRequest() { 
     setTimeout(function() { 
      var val = Math.floor(Math.random() * (MAXNUM+1)); 
      if (val !== 0) 
       newAsyncRequest(); 
      console.log(val); 
     }, 1000); 
    } 

    newAsyncRequest(); 

}); 
+0

+1是的,那是我想要的。我會試着理解它並在我的插件中實現它。當我完成時,我會發布最終答案。謝謝! – synack 2012-07-11 15:01:29

+0

我認爲解析完成模式可以使用jsdeferred的調用函數:[jsdeferredAPI](http://cho45.stfuawsc.com/jsdeferred/doc/Deferred.html#Deferred#call) – synack 2012-07-11 15:15:31

+1

@ Kits89實現看看** jsdeferredAPI **。 jsdeferred中的'call'似乎相當於jQuery.Deferred中的'resolve'。同樣,'next' *似乎*在jQuery.Deferred中相當於'done',儘管不太確定。首先驗證上面的代碼片段是否適用於使用[jsdeferredAPI](http://cho45.stfuawsc.com/jsdeferred/doc/Deferred.html#Deferred#call)而不是[jQuery]後可能是一個好主意。推遲(http://api.jquery.com/category/deferred-object/)。 – 2012-07-11 15:30:53

2

謝謝你們的幫助。這就是我所做的:

var sess = ...; 
Deferred.define(this);  
function asyncFunction (session) { 
    Deferred.next(function() { 
     var d = new Deferred(); 
     (new Request({ 
       url: form.php, 
       content: "sess=" + session, 
       onComplete: function (response) { 
        d.call(response.json); 
       } 
     })).get(); 
     return d; 
    }).next(function(resp) { 
     if (resp.id) { 
      asyncFunction(session); 
      console.log(resp.msg);    
     }   
    }); 
} 

asyncFunction(sess); 
+0

gr8!如果問題得到解決,請將問題標記爲已回答。 – 2012-07-11 16:10:19

2

你爲什麼不使用setInterval循環?在基於SDK擴展的情況下,這看起來像:

https://builder.addons.mozilla.org/addon/1065247/latest/

的承諾狀花紋在使用定時器最大的好處是,你可以做並行的東西,並使用更爲複雜的依賴關係爲各種任務。像這樣一個簡單的循環就像使用setInterval一樣簡單/整齊地完成。

+0

+1我喜歡它,但想象一下這個請求需要的時間超過150毫秒。然後,循環的一些迭代將會異步重疊,對吧?我不想重疊...我想要它序列化。 – synack 2012-07-12 07:47:38

+0

順便說一句,我發現這個有用的教程[API承諾](https://addons.mozilla.org/en-US/developers/docs/sdk/1.7/packages/api-utils/promise.html)。但是我無法將承諾庫導入到我的插件中。 – synack 2012-07-12 09:56:23

+0

對於SDK自己的承諾庫,您可以像這樣導入它:const {reject} = require('api-utils/promise');這不行嗎? – canuckistani 2012-07-12 22:28:31