【问题标题】:sequential function call, while one of them uses setTimeout顺序函数调用,其中一个使用 setTimeout
【发布时间】:2011-08-09 10:33:00
【问题描述】:

我想依次调用三个函数someTask1someTask2someTask3。然而,函数someTask2 涉及Ajax 调用,并使用setTimeout 递归调用自身,除非返回desired 值。代码如下所示:

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

正如您可能已经猜到的那样,此代码的执行不会在调用someTask3 之前等待someTask2 返回。

我希望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 只有一个执行线程。一次只能运行一个任务,尽管浏览器内部可以使用不同的线程进行记账,例如跟踪计时器

标签: javascript settimeout


【解决方案1】:

您可以在 jquery 中使用 Deferred 实现此目的:

$.when(someTask1(), someTask2(), someTask3()).then(successFunc, errorFunc);

您需要返回一个带有承诺值的自定义 .Deferred 对象。

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);
  • 感谢您的回答,我学到了一两件事。但是问题仍然存在。$.when(someTask2(someObj), someTask3(someObj)).then(function(){alert('finished');}); 函数仍然没有按顺序调用。可能是 setTimeout 造成的问题。
  • +1 用于介绍一个非常酷的功能。更新了问题。
  • 所有函数都需要是 Deferred 对象的实例。从 jQuery 1.5 开始, .ajax 对象会自动实现该对象,因此您无需将其设为 Deferred 对象。或者,您可以执行以下操作:$(someTask1(), someTask2()).then(function() { someTask3(); }, errorFunc)
  • 是的。继续阅读。这是一篇我认为值得一提的好文章,Using Deferreds in jQuery 1.5
【解决方案2】:

您可以将回调传递给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); });

【讨论】:

  • 是的,这将是最后的手段。我真的很想避免它。更新的问题,顺便说一句。
  • 我不会害怕采用这样的方法。在使用 jQuery 和 Ajax 时,回调是一种非常常见的模式,因为您经常需要在异步操作之后执行一些函数。也就是说,如果您使用 jQuery 1.5,Deferred 可能是更好的选择——尤其是一旦(如果)您在应用程序的其他地方执行此操作。
  • 当然。 .ajax 是一个延迟对象,不利用它的强大功能将是愚蠢的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-22
  • 2023-03-26
  • 1970-01-01
  • 2022-06-14
  • 2022-12-05
相关资源
最近更新 更多