jQuery 延迟:在方法中嵌套承诺,最后一次回调太早

jQuery deferred: Nesting promises in methods, last callback too early

我对 deferreds/promises 有疑问。我有一个主函数,它调用两个必须串行执行的函数。第一个包含一堆必须首先串行执行的异步 Web 服务调用。但实际上我的第二个函数是在第一个函数中的每个异步反馈都存在之前被调用的。

我构建了一个小示例来说明我的问题:https://fiddle.jshell.net/qLf1c0uq/

参考代码如下:

function first() {
  $('ul').append("<li>first started</li>");
  let deferred = $.Deferred();
  setTimeout(function() { // Any async function.
    $('ul').append("<li>first ended</li>");
    deferred.resolve();
  }, 2000);
  return deferred.promise();
}

function second(da) {
  $('ul').append("<li>second started</li>");
  let deferred = $.Deferred();
  $('ul').append("<li>second ended</li>");
  deferred.resolve();
  return deferred.promise();
}

function third(da) {
  $('ul').append('<li>third started</li>')
  let deferred = $.Deferred();
  setTimeout(function() {
    $('ul').append("<li>third ended</li>");
    deferred.resolve();
  }, 2000);

  return deferred.promise();
}

function GroupFunction1() {
  let deferred = $.Deferred();
  $('ul').append("<li>Group 1 started</li>");
  var data = "test2";
  $.when(first()).then(function() {
    second(data);
  }).then(function() {
    third("test2");
  }).then(function() {
    $('ul').append("<li>Group 1 ended</li>");
    deferred.resolve();
  });

  return deferred.promise();
}

function GroupFunction2() {
  $('ul').append("<li>Group 2 started</li>");

  $('ul').append("<li>Group 2 ended</li>");
}

$(function() {
  $.when(GroupFunction1()).then(GroupFunction2);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

GroupFunction2 在第三个函数结束之前启动。反之亦然。

有人可以帮忙吗?

谢谢!

您需要 return 回调函数中的承诺。这是 promise 链可以等待它们的唯一方式。

此外,您正在滥用 Explicit Promise Creation 反模式。

.then()return是一个承诺。你可以 return 那个。并且将单个承诺传递给 $.when() 没有意义,因为 $.when() 要做的就是立即吐出它:

function first() {
  $('ul').append("<li>first started</li>");
  let deferred = $.Deferred();
  setTimeout(function() { // Any async function.
    $('ul').append("<li>first ended</li>");
    deferred.resolve();
  }, 2000);
  return deferred.promise();
}

function second(da) {
  $('ul').append("<li>second started</li>");
  let deferred = $.Deferred();
  $('ul').append("<li>second ended</li>");
  deferred.resolve();
  return deferred.promise();
}

function third(da) {
  $('ul').append('<li>third started</li>')
  let deferred = $.Deferred();
  setTimeout(function() {
    $('ul').append("<li>third ended</li>");
    deferred.resolve();
  }, 2000);

  return deferred.promise();
}

function GroupFunction1() {
  let deferred = $.Deferred();
  $('ul').append("<li>Group 1 started</li>");
  var data = "test2";

  // v-- here
  return first()      // v-- here
    .then(function() { return second(data); })
    .then(function() { return third("test2"); })
    .then(function() { $('ul').append("<li>Group 1 ended</li>"); });
}

function GroupFunction2() {
  $('ul').append("<li>Group 2 started</li>");

  $('ul').append("<li>Group 2 ended</li>");
}

$(function() {
  GroupFunction1().then(GroupFunction2);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

不是你问题的真正答案; JLRishe 已经解释了为什么您的代码没有等待您的承诺。

这更像是一个清理工作,我包含了一个将 setTimeout 包装到 Promise 中的小实用程序,这样您就不必手动操作,也不必到处重复自己的操作。

function first(data) {
  $('ul').append("<li>first started</li>");
  return wait(2000)
    .then(function(){
      $('ul').append("<li>first ended</li>");
    });
}

function second(data) {
  $('ul').append("<li>second started</li>");
  $('ul').append("<li>second ended</li>");
  //let's stay consistent with our API
  //even if we have to return an empty/pointless Promise for that
  return Promise.resolve();
}

function third(data) {
  $('ul').append("<li>third started</li>");
  return wait(2000)
    .then(function(){
      $('ul').append("<li>third ended</li>");
    });
}


function GroupFunction1() {
  $('ul').append("<li>Group 1 started</li>");
  var data = "test2";
  return first(data)
    //.then(wait(1000))  //maybe a little pause in between?
    .then(function(){
      return second(data);
    })
    //.then(wait(1000))  //maybe a little pause in between?
    .then(function(){
      return third(data);
    })
    .then(function() {
      $('ul').append("<li>Group 1 ended</li>");
    });
}

function GroupFunction2() {
  $('ul').append("<li>Group 2 started</li>");

  $('ul').append("<li>Group 2 ended</li>");
  return Promise.resolve(); //again, just staying consistent
}

//DRY. Get rid of all the manual setTiemouts and the Deferreds and wrap them into a function:

//Examples on how to use:
//promise = somePromise.then(wait(1000)).then(someFn);
//promise = wait(1000).then(someFn);
//promise = wait(1000).resolve(42);
//promise = wait(30000).reject("timeout");
const wait = (function(){
 const reject = Promise.reject.bind(Promise);
 const methods = {
  then(a,b){ return this().then(a,b) },
  resolve(value){ return this(value) },
  reject(err){ return this(err).then(reject) }
 };
 return delay => Object.assign(value => new Promise(resolve => void setTimeout(resolve, delay, value)), methods);
})();

$(function() {
  GroupFunction1().then(GroupFunction2);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>