链式jQuery延迟,即使在then语句之后也会调用所有进度回调

时间:2014-07-23 21:16:29

标签: jquery jquery-deferred

我试图基于jquery延迟对象链接一些调用。为简单起见,我想:

  1. 调用返回延迟对象的异步方法
  2. 观察其进展
  3. 完成后,调用另一种方法
  4. 观察其进展
  5. 起初,我写了类似的东西:

    myFirstFunction()
      .progress(myFirstProgressCallback)
      .done(myFirstDoneCallback)
      .then(mySecondFunction)
      .progress(mySecondProgressCallback)
      .done(mySecondDoneCallback);
    

    但是我发现了一些我没想到的东西(在阅读文档之后,它似乎是它的工作方式):

      只有myFirstFunction解析其延迟对象后才会调用
    1. myFirstDoneCallback
    2. mySecondDoneCallback也只调用一次
    3. 但当myFirstFunction和mySecondFunction在他们自己的延迟对象上调用notify时,会调用mySecondProgressCallback。
    4. 示例you can run it in this jsbin

      function async(number){
        var def = new $.Deferred();
      
        setTimeout(function(){
          def.notify("Hello from " + number);
        }, 300);
      
        setTimeout(function(){
          def.resolve("I'm done " +number);
        }, 600);
      
        return def.promise();
      }
      
      async(1)
        .progress(function(msg){
          console.log("First progress: " + msg);
        })
        .done(function(msg){
          console.log("First done: " +msg);
        })
        .then(function(){
          return async(2);
        })
        .progress(function(msg){
          console.log("Second progress: " + msg);
        })
        .done(function(msg){
          console.log("Second done: " +msg);
        });
      

      控制台中的结果:

      "First progress: Hello from 1"
      "Second progress: Hello from 1"
      "First done: I'm done 1"
      "Second progress: Hello from 2"
      "Second done: I'm done 2"
      

      第一反应:"为什么地狱??????"

      第二:"我怎样才能做我想做的事?"

      我用这个替换了我的代码,效果很好(jsbin):

      function async(number){
        var def = new $.Deferred();
      
        setTimeout(function(){
          def.notify("Hello from " + number);
        }, 300);
      
        setTimeout(function(){
          def.resolve("I'm done " +number);
        }, 600);
      
        return def.promise();
      }
      
      async(1)
        .progress(function(msg){
          console.log("First progress: " + msg);
        })
        .done(function(msg){
        console.log("First done: " +msg);
      })
      .then(function(){
        return async(2)
          .progress(function(msg){
            console.log("Second progress: " + msg);
          })
          .done(function(msg){
            console.log("Second done: " +msg);
          });
        });
      

      输出:

      "First progress: Hello from 1"
      "First done: I'm done 1"
      "Second progress: Hello from 2"
      "Second done: I'm done 2"
      

      如何避免在"然后"内部的函数内注册进度回调?声明?

1 个答案:

答案 0 :(得分:1)

这个想法可能对你有用:检查回调中的上下文。

默认情况下,回调的上下文是触发操作的承诺:

var async2,
    async1 = async(1);

async1
    .done(function (msg) {
        if (this === async1) {
            console.log("First done: " + msg);
        }
    })
    .fail(function (msg) {
        if (this === async1) {
            console.log("First fail: " + msg);
        }
    })
    .progress(function (msg) {
        if (this === async1) {
            console.log("First progress: " + msg);
        }
    })
    .then(function (msg) {
        async2 = async(2);
        return async2;
    })
    .done(function (msg) {
        if (this === async2) {
            console.log("Second done: " + msg);
        }
    })
    .fail(function (msg) {
        if (this === async2) {
            console.log("Second fail: " + msg);
        }
    })
    .progress(function (msg) {
        if (this === async2) {
            console.log("Second progress: " + msg);
        }
    });

我不确定这是否比在then内嵌套进度回调更好。一个问题是,可以使用特定上下文执行操作(使用notifyWithresolveWithrejectWith)。

比您要求的更多信息

我刚才发现了同样的行为,感受到了同样的挫败感,并且达到了你所做的同样的分辨率。从那以后,我对通知/进展的工作方式做了一些研究,这就是我发现的:

then会返回一个新的承诺,但它也会将前承诺中的所有操作(resolverejectnotify)转发给后一个承诺。事实上,只要您向承诺链添加错误处理,您就会发现此行为也会扩展到fail回调:

function async(number){
  var def = new $.Deferred();

  setTimeout(function(){
    def.notify("Hello from " + number);
  }, 300);

  setTimeout(function(){
    def.reject("I've failed " + number);
  }, 450);

  return def.promise();
}

async(1)
  .progress(function(msg){
    console.log("First progress: " + msg);
  })
  .fail(function(msg){
    console.log("First fail: " +msg);
  })
  .then(function(){
    return async(2);
  })
  .progress(function(msg){
    console.log("Second progress: " + msg);
  })
  .fail(function(msg){
    console.log("Second fail: " +msg);
  });

输出:

"First progress: Hello from 1"
"Second progress: Hello from 1"
"First fail: I've failed 1"
"Second fail: I've failed 1"

即使永远不会调用第二个async,也会执行所有progressfail回调。同样的事情虽然很少,但如果你为它提供除函数之外的任何东西,那么就会出现完成处理程序:

async(1)
  .progress(function(msg){
    console.log("First progress: " + msg);
  })
  .done(function(msg){
    console.log("First done: " +msg);
  })
  .then('foo')
  .progress(function(msg){
    console.log("Second progress: " + msg);
  })
  .done(function(msg){
    console.log("Second done: " +msg);
  });

输出:

"First progress: Hello from 1"
"Second progress: Hello from 1"
"First done: I'm done 1"
"Second done: I'm done 1"

所以我想我想说的是,你在进程回调中看到的行为与Deferred对象的工作方式并不矛盾。

在我的调查开始时,我乐观地认为我们可以使用Promises/A+样式激发我们想要的行为:promise.then(doneFilter, failFilter, progressFilter)

async(1)
    .then(function (msg) {
        console.log("First done: " + msg);
        return async(2);
    },
    null /*Failure Handler*/,
    function (msg) {
        console.log("First progress: " + msg);
    })
    .then(function (msg) {
        console.log("Second done: " + msg);
    },
    null /*Failure Handler*/,
    function (msg) {
        console.log("Second progress: " + msg);
    });

不幸的是,结果并不好:

"First progress: Hello from 1"
"Second progress: undefined"
"First done: I'm done 1"
"Second progress: Hello from 2"
"Second done: I'm done 2"

有趣的是,第二次进度回调的第一次执行没有提供正确的值。我没有进一步调查,除了确认Q(支持进度/通知的承诺的另一个实现)提供相同的结果。

最后,我回答了一个问题,帮助我澄清了为什么这一切都有效:

如果所有操作都转发到下一个承诺,为什么这些转发的操作不会调用嵌套的进度处理程序?

解决了第一个承诺并且下一个异步任务待处理后,progress处理程序被添加为回调。与donefail不同,progress处理程序需要在执行相应操作(notify)时附加。