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

时间:2021-02-12 00:35:20

标签: javascript promise settimeout

我想一次执行一个函数,并在一个函数完成时调用另一个函数。我能够使用回调来做到这一点,但不能使用承诺链。 这是我尝试过的(基于 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);
}

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

2 个答案:

答案 0 :(得分:5)

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

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

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()

另一种更清晰的方法:

您还可以让您的显示函数返回一个 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)

答案 1 :(得分:1)

displayAll 的执行顺序演练:

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

Promise 构造函数调用 display1 设置超时以记录“display1”并解决承诺。这完美地工作,并且最初的 1 秒延迟得到尊重。

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

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

  2.  .then(display4)));
    

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

然后调用 displayAll 的预期输出是

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

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

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

顺便说一句,承诺链的值是链中最后一个 thencatchfinally 调用返回的承诺 - 承诺链值可能由一个函数,但至少在实践中很少记录在变量中。