11

我有一个充满这样功能的对象:

var functions = {
    fun1 : function(){ ... }
    fun2 : function(){ ... }
    fun3 : function(){ ... }
};

对象键都在数组中引用,如下所示:

var funList = ['fun1','fun2','fun3'];

我一直在使用数组来运行所有功能:

$.each(funList, function(i,v){
    functions[v].call(this, args);
});

我的问题是,我需要一些方法来推迟所有功能的运行,例如:

  1. 在 $.each 循环中,函数串行运行
  2. 一些方法可以将后续代码的运行推迟到数组/对象中的所有函数都完成之后。

我已经读过我应该为此使用 $.map 方法,但是我很难将注意力集中在它上面。

4

2 回答 2

8

哇……那是一个练习——$.Deferred() 有点难以让我的思绪全神贯注,但我让它按照我想要的方式工作!我不知道这是多么精简——也许其他人可以让它更有效率。

它的要点是使用 .pipe() 创建一个延迟链。

编辑:我的代码(如下)在参考键列表中不容纳少于 2 个对象。我更新了 jsfiddle 以使用任何大小的键列表。


您可以在这里看到所有在 JSFiddle 上的工作:http: //jsfiddle.net/hBAsp/3/


这是我如何解决它的分步说明:

  1. 从一个充满函数的对象和一组引用键开始(按照您希望它们处理的顺序)。这些函数应该期望收到一个延迟对象以在完成时解析:

    var obj = {
        one: function(dfd){
            /* do stuff */
            dfd.resolve();
        },
        two: function(dfd){...},
        three: function(dfd){...},
        etc...
    };
    var keys=['one','two','three', etc...];`
    
  2. 创建将承诺传递给初始化函数的主要延迟包装器。我们将在执行过程中将代码添加到函数中:

    var mainDeferred = $.Deferred(function(mainDFD){
    
  3. 在该初始化函数中,创建一个延迟数组,手动创建第一个延迟对象:

    var dfds = [$.Deferred()];
    
  4. 接下来,使用 for 循环遍历我们的键列表中的倒数第二个项目。我们将:

    1. 为我们逐步执行的每个项目创建一个延迟对象
    2. 设置一个匿名函数,它将从我们的 obj 运行与键相关的函数,传递给它以解析我们新创建的 Deferred 对象
    3. 将新创建的函数传递到先前创建的 Deferred 对象上(这就是我们必须手动创建第一个对象的原因)
    4. 您必须在 for 列表中使用封闭循环来解决 JavaScript 范围问题

      for (i=1; i<keys.length-1; i++){
          (function(n){
              dfds.push($.Deferred());
              dfds[i-1].pipe(function(){
                  obj[keys[n]].call(this,dfds[n]);
              });
          })(n);
      };
      
  5. 手动创建最后一个匿名函数并将其通过管道传递到我们列表中的倒数第二个延迟对象。我们手动执行此操作,因为我们希望将主要的延迟对象传递给它以进行解析,以便在最后一个进程运行后整个 shebang 将立即触发:

    dfds[keys.length-2].pipe(function(){
        obj[keys[keys.length-1]].call(this,mainDFD);
    });
    
  6. 现在我们的整个管道已经构建好了,我们所要做的就是触发第一个对象并将其分配为第一个延迟解析:

    obj[keys[0]].call(this,dfds[0]);
    
  7. 只需要关闭我们的主要延迟初始化函数(这将在创建延迟后立即触发:

    });
    
  8. 现在我们还可以通过管道将一个函数传递给主对象,以便在我们之前的所有元素都运行后运行:

    mainDeferred.pipe(function(){
        /* do other stuff */    
    });
    
于 2012-08-09T18:01:59.003 回答
1

我认为您可以使用管道将回调函数作为数组传递。这是一个例子

var a = $.Deferred();

var b = $.Deferred();

var c = $.Deferred();

var checkA = function() {
  console.log('checkA');
  return a.resolve();
};


var checkB = function() {
  console.log('checkB');
  return b.resolve();
};

var checkC = function() {
  console.log('checkC');
  return c.reject();
};

checkAll = $.Deferred().resolve().promise();
checkAll = checkAll.pipe(checkA);
checkAll = checkAll.pipe(checkB);
checkAll = checkAll.pipe(checkC);

checkAll.done(function() {
  return console.log('checkAll done');
}).fail(function() {
  return console.log('checkAll fail');
});

输出将是

"checkA"
"checkB"
"checkC"
"checkAll fail"

如果您想查看不同的结果,您可以在检查功能中将结果“解决”更改为“拒绝”。例如:如果您将 checkA 更改为拒绝。

var checkA = function() {
  console.log('checkA');
  return a.reject();
};

输出将是

"checkA"
"checkAll fail"

它不会去 checkB 和 checkC 因为 checkA 被拒绝。这样您就可以使用数组调用该函数

funList = [checkA, checkB, checkC];
for(var i=0; i<funList.length; i++){
  checkAll = checkAll.pipe(funList[i]);
}

笔记。您必须确保回调始终返回 Deferred 对象。

于 2015-07-06T09:07:18.807 回答