1

问题简介

我需要在循环中调用异步函数,直到满足条件。这个特定的函数向网站form.php发送一个 POST 请求,并对响应执行一些操作,响应是一个 JSON 字符串,表示具有id字段的对象。因此,当该 id 为空时,外部循环必须结束。该函数执行以下操作:

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

4 回答 4

2

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

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

于 2012-07-11T14:03:32.383 回答
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);
于 2012-07-11T15:38:54.740 回答
2

为什么不直接使用 setInterval 循环?在基于 SDK 的扩展的情况下,这看起来像:

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

与使用计时器相比,类 Promise 模式的最大好处是您可以并行执行操作,并为各种任务使用更复杂的依赖关系。像这样的简单循环可以使用 setInterval 轻松/巧妙地完成。

于 2012-07-11T21:31:59.630 回答
1

如果我正确理解您想要做什么,那么Deferred是一个好方法。这是一个使用jQuery的示例,它内置了 Deferred 功能 ( 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的回答是正确的。对于这个问题,解决方案更简单。在不使用 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();

});
于 2012-07-11T14:40:56.650 回答