顺序函数调用,其中一个使用setTimeout

4

我希望按顺序调用三个函数someTask1someTask2someTask3。然而,函数someTask2包含Ajax调用,并且除非返回所需的值,否则使用setTimeout递归地调用自身。 代码如下:

doListOfTasks: function(myparam){
    var someObj = someTask1(myParam);
    someTask2(someObj);
    someTask3(someObj);
},
someTask2: function(someObj){
    $.ajax({
          url: "someUrl.do",
          type: "POST",
          data: ({"id": rowObject.instanceId}),
          dataType: "json",
          async:false,
          success: function(res){
              if(res.prop1 != 'desired'){
                  setTimeout(function(){someTask2(someObj);}, 2000);
              }
          }
       }
    );
},

正如您所猜测的那样,执行此代码时不会等待someTask2返回后才调用someTask3

我希望在doListOfTasks内部执行代码顺序。我该怎么做呢?

此外,我不想在success回调函数中硬编码someTask3。例如,我不想这样做:

          success: function(res){
              if(res.prop1 != 'desired'){
                  setTimeout(function(){someTask2(someObj);}, 2000);
              }else{
                  someTask3(someObj);
              }

          }

如何实现这个功能?

谢谢。


编辑#1

问题不在于无法调用函数...而是同步的问题。我希望someTask2完成它正在做的事情,然后才调用someTask3

someTask2使用setTimeout重复调用自身...我猜这会触发一个新线程,并且someTask2在第一次调用后就返回...在主线程中触发someTask3。但是,在每次调用setTimeout时都会生成(并被销毁)单独的线程,直到满足所需条件。

这就是为什么当someTask2仍在循环时,调用someTask3的原因。

不确定我是否正确。


你的理解基本上是正确的,尽管 JavaScript 只有一个执行线程。只能同时运行一个任务,尽管在内部浏览器可以使用不同的线程来进行簿记,例如跟踪计时器。 - Justin Ethier
2个回答

2
您可以使用jQuery中的Deferred来实现此功能:
$.when(someTask1(), someTask2(), someTask3()).then(successFunc, errorFunc);

你需要返回一个自定义的 .Deferred 对象,其中包含一个 promise 值。
someTask2: function(someObj)
{
    var def = $.Deferred();

    $.ajax({
          url: "someUrl.do",
          type: "POST",
          data: ({"id": rowObject.instanceId}),
          dataType: "json",
          async:false,
          success: function(res){
              if(res.prop1 != 'desired'){
                  setTimeout(function(){someTask2(someObj);}, 2000);
              }
              else
              {
                def.resolve(res);
              }
          }
       }
    );

    return def.promise();
}

例如:

function someTask1()
{
    var def = $.Deferred();
    def.resolve('i\'m data resolved from task1');
    return def.promise();
}

function someTask2(someObj)
{
    var def = $.Deferred();

    var count = 0;
    var f = function() {
        console.log(++count);
        if (count > 2) {
            def.resolve('whoop we got the value we wanted in task 2 after many tries: ' + count);
        }
        else
            setTimeout(f, 1000);
    };
    f();
    return def.promise();
}

function someTask3()
{
    var def = $.Deferred();
    def.resolve('and hello from task3!');
    return def.promise();  
}

var success = function(x) {
  console.log('success:', arguments);
};

var error = function() {
      console.log('oh no an error occured in one of the tasks.');
};

$.when(someTask1(), someTask2(), someTask3()).then(success , error);

将会显示

1
2
3
success: ["i'm data resolved from task1",
          "whoop we got the value ...k 2 after many tries: 3",
           "and hello from task3!"]

可用的代码示例: http://jsfiddle.net/garreh/29SW7/


记得更改你的任务,使它们像 yourObject.someTask2(someObj); 这样。 - Gary Green
谢谢你的回答,我学到了一些东西。但问题仍然存在。$.when(someTask2(someObj), someTask3(someObj)).then(function(){alert('finished');});这些函数仍然没有按顺序调用。可能是setTimeout造成的问题。 - Nishant
所有的函数都需要是Deferred对象的实例。从jQuery 1.5开始,.ajax对象自动实现了该对象,因此您不需要将其作为Deferred对象。或者,您可以像这样做:$(someTask1(), someTask2()).then(function() { someTask3(); }, errorFunc) - Gary Green
我已更新答案,希望能够帮助你与你的新的Deferred朋友走上正确的轨道;-) - Gary Green
这非常棒。非常感谢你。标记为已接受的答案。 - Nishant
显示剩余2条评论

1

你可以将回调函数传递给someTask2。例如:

someTask2: function(someObj, callback){
$.ajax({
      url: "someUrl.do",
      type: "POST",
      data: ({"id": rowObject.instanceId}),
      dataType: "json",
      async:false,
      success: function(res){
          if(res.prop1 != 'desired'){
              setTimeout(function(){someTask2(someObj, callback);}, 2000);
          } else {
             if (callback != null) { 
                 callback(); 
             }
          }
      }
   }
);
}

然后只需将someTask3作为回调函数传递:

someTask2(someObj, function (){ someTask3(someObj); });

是的,这将是最后的办法...我真的想避免它。顺便说一下,问题已经更新了。 - Nishant
我不会害怕采用这样的方法。在使用jQuery和Ajax时,回调是一种非常常见的模式,因为通常你需要在异步操作后执行一些函数。话虽如此,如果你正在使用jQuery 1.5,延迟对象(Deferred)可能是一个更好的选择,尤其是当你在应用程序的其他地方也这样做时。 - Justin Ethier
当然。.ajax 是一个延迟对象,不利用它强大的功能是很愚蠢的。 - Gary Green

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接