承诺链不会等到其他承诺被解决

promise chain does not wait until other promise is resolved

我想一次执行一个函数,并在一个函数完成时调用另一个函数。我能够使用回调而不是使用承诺链来做到这一点。 这是我尝试过的(基于 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#chained_promises),但它同时执行前 3 个函数,而不是在每个函数中等待 1 秒:

function displayAll() {
    var myPromise = (new Promise(display1))
    .then((new Promise(display2))
    .then((new Promise(display3))
    .then(display4)));
}

function display1(resolve) {
    setTimeout(function () {
        console.log("display1");
        resolve();
    }, 1000);
}

function display2(resolve) {
    setTimeout(function () {
        console.log("display2");
        resolve();
    }, 1000);
}

function display3(resolve) {
    setTimeout(function () {
        console.log("display3");
        resolve();
    }, 1000);
}

function display4(resolve) {
    setTimeout(function () {
        console.log("display4");
    }, 1000);
}

你知道代码有什么问题吗?是否可以在没有回调的情况下完成我想做的事情?

为了链接 Promises (MDN),您需要在 then 方法回调中 return 一个承诺,而不是将承诺构建为 then 方法的参数.

这将在“遇到”new 关键字时立即触发 Promise,这不是预期的行为。相反,您希望等待第一个 Promise 结束,然后链接 then 方法,该方法将创建一个新的 Promise:

function displayAll() {
    var myPromise = (new Promise(display1))
    // .then(new Promise(display2)) <-- you are calling here the promise
    .then(function() {
         return new Promise(display2) // <-- here we return a promise to chain
     })
    .then(()=> new Promise(display3)) // same with arrow functions
    .then(display4);
}

来自您的代码:

function displayAll() {
    var myPromise = (new Promise(display1))
    .then(()=> new Promise(display2))
    .then(() => new Promise(display3))
    .then(display4);
}

function display1(resolve) {
    setTimeout(function () {
        console.log("display1");
        resolve();
    }, 1000);
}

function display2(resolve) {
    setTimeout(function () {
        console.log("display2");
        resolve();
    }, 1000);
}

function display3(resolve) {
    setTimeout(function () {
        console.log("display3");
        resolve();
    }, 1000);
}

function display4(resolve) {
    setTimeout(function () {
        console.log("display4");
    }, 1000);
}

displayAll()

另一种更清晰的方法:

您还可以将显示函数 return 设为 Promise,这样您就可以将它们直接传递给 then 方法:

function display1() {
   return new Promise(resolve => {
      setTimeout(function () {
         console.log("display1");
         resolve();
      }, 1000);
   });
}
function display2() {
   return new Promise(resolve => {
      setTimeout(function () {
         console.log("display2");
         resolve();
      }, 1000);
   });
}
function display3() {
   return new Promise(resolve => {
      setTimeout(function () {
         console.log("display3");
         resolve();
      }, 1000);
   });
}
function display4() {
   return new Promise(resolve => {
      setTimeout(function () {
         console.log("display4");
         resolve();
      }, 1000);
   });
}

let myPromise = 
      display1()
        .then(display2)
        .then(display3)
        .then(display4)

displayAll 的执行顺序演练:

  1. var myPromise = (new Promise(display1))
    

Promise 构造函数调用 display1 设置超时以记录“display1”并解析承诺。这非常有效,最初的 1 秒延迟得到尊重。

  1.  .then(new Promise(display2))
    
  • 在执行displayAll时调用myPromisethen方法。
  • then 的参数在调用前计算。
  • 创建 then 的 promise 参数会导致 Promise 构造函数调用 display2,从而设置相对于 displayAll 的执行时间的超时。
  • 调用时 then 静静地忽略 promise 参数,因为它不是函数。默认处理程序,由 then 在没有可调用参数的情况下使用,传递传入数据或承诺拒绝原因。
  1.  .then(new Promise(display3))
    

    与前面的 then 子句的操作相同:设置一个相对于 displayAll 执行时间的计时器,并使用传递数据或拒绝原因的默认处理程序。

  2.  .then(display4)));
    

    display4 注册为处理程序,当在第 3 步中 returned 的承诺变为 fullfilled 时将被调用。 display4 设置一个可行的计时器来记录“display4”。注意 display4 的参数现在被错误命名 - 传递给后续履行处理程序的参数是链中前一个承诺处理程序的值 return。

调用 displayAll 的预期输出是

  1. 调用 displayAll 后延迟一秒钟并记录“display1”。
  2. 延迟一秒钟,在 displayAll 被调用后再次记录“display2”。
  3. 延迟一秒钟,在 displayAll 被调用后再次记录“display3”。
  4. 当 promise 链处理作为处理程序执行 display4 时,设置一个计时器来记录“display4”——因此它在“display3”之后记录一秒。

一种错开显示函数执行的解决方案是将承诺创建从 then 计算参数的位置移动到处理程序本身。处理程序可以 return 承诺延迟沿着承诺链继续进行,直到 returned 承诺被计时器解决:

 function displayN() {
     return new Promise( resolve => 
         setTimer( function() {
            console.log("displayN");
            resolve();   
         }, 1000)
     );
 }

其他解决方案和方法也是可能的。承诺 setTimeout 以创建在指定时间间隔后解决的承诺对于延迟承诺链或 async 函数中的步骤很有用。作为一个相当小的例子:

 function delayPromise( msec) {
     return new Promise(resolve=> setTimeout( resolve, msec));
 }

顺便说一句,promise 链的值是链中最后一个 thencatchfinally 调用的 promise return - promise 链值可能 return 由函数编辑,但实际上至少很少记录在变量中。