循环调用异步函数

时间:2012-07-11 13:58:37

标签: javascript firefox-addon firefox-addon-sdk

问题简介

我需要在循环中调用异步函数,直到满足条件。此特定函数向网站 form.php 发送POST请求,并使用响应执行某些操作,该响应是表示具有 id 字段的对象的JSON字符串。因此,当该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中的相应参数调用相同的函数。

4 个答案:

答案 0 :(得分:2)

您的问题并不完全清楚,但基本体系结构必须是异步操作的完成事件处理程序必须决定是再次尝试还是简单地返回。如果操作的结果需要另一次尝试,那么处理程序应该调用父函数。如果没有,那么通过简单地退出循环就会结束。

你不能在JavaScript中编写类似这样的东西,看起来像一个简单的“循环”结构,因为操作是异步。操作的结果不会以允许循环机制对结果进行测试的方式发生;在结果可用之前,循环可能会运行数千次迭代。换句话说,你不要“等待”代码的异步操作。等待什么都不做,并允许已注册的事件处理程序在结果准备就绪时接管。

答案 1 :(得分: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);

答案 2 :(得分:2)

为什么不使用setInterval循环?对于基于SDK的扩展,这看起来像:

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

与使用计时器相似的承诺模式的一大好处是,您可以并行执行任务,并为各种任务使用更复杂的依赖项。像这样的简单循环就像使用setInterval一样容易/整齐地完成。

答案 3 :(得分: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。对于这个问题,解决方案更简单。不使用Deferred,上面的代码片段变为以下内容。对不起,我带你去找一个更难的解决方案。

$(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();

});