【问题标题】:How to successfully loop in the middle of a promise chain如何在承诺链中间成功循环
【发布时间】:2018-02-17 09:01:28
【问题描述】:

我正在整理一个承诺链,对此很抱歉。我想要做的是,对于任何特定功能中的任何错误,我想等待一秒钟再试一次,直到它成功,然后按照原始计划继续使用承诺链。

我尝试了一个 if 语句,如果有错误消息则再次调用该函数,如果没有则解决,但这不会继续成功的 Promise 链。

然后我设计了一个解决方案,其中包含一个内置到函数中的 for 循环,如下所示:

var firstMethod = function() {
    var promise = new Promise(function(resolve, reject){
       setTimeout(function() {
          console.log('first method completed');
          resolve();
       }, 1000);
    });
    return promise;
 };

var secondMethod = function(someStuff) {
    var promise = new Promise(function(resolve, reject){
        for(let randomNumber ; randomNumber < .9||randomNumber == null ;  )
            {   
                setTimeout(function() {
                    randomNumber = Math.random();
                    console.log('the number wasn\'t big enough');
                    if (randomNumber>.9)
                    {  
                        console.log("got a number big enough");
                        resolve();
                    }
                }, 1000)
            }
        });
    return promise;
};

var thirdMethod = function(someStuff) {
    var promise = new Promise(function(resolve, reject){
       setTimeout(function() {
          console.log('third method completed');
          resolve();
       }, 1000);
    });
    return promise;
 };

 firstMethod()
    .then(secondMethod)
    .then(thirdMethod);

它完成了 firstMethod,然后在我尝试运行它时挂起。如果我在 secondMethod 中注释掉 setTimeout 它可以工作,但我觉得这并没有模拟我想要使用它的真实案例场景。

我哪里错了?有没有更好的方法来循环中间承诺链,直到达到特定结果,然后继续使用承诺链?

【问题讨论】:

  • for 循环将永远运行。 setTimeout(resolve, 10000 * Math.random()) 呢?
  • 异步“循环”总是需要递归。您的 for 循环会立即运行。
  • 最简单的解决方案是使用带有简单循环的async/await 语法。只为 setTimeout 做出承诺 - 永远不要在 new Promise 回调中添加任何逻辑!

标签: javascript node.js


【解决方案1】:

您可以将 secondMethod 替换为

var secondMethod = function (someStuff) {
    var promise = new Promise(function (resolve, reject) {
        let randomNumber;
        (function doItUntilGetEnoughBigNumber() {
            setTimeout(function () {
                randomNumber = Math.random();

                if (randomNumber > .9) {
                    console.log("got a number big enough", randomNumber);
                    resolve();
                } else { // edited: message moved her  
                    console.log('the number wasn\'t big enough', randomNumber);
                    doItUntilGetEnoughBigNumber();
                }
            }, 1000)
        })()
    });
    return promise;
};

你的代码不起作用的原因,因为停止for循环的条件是异步的,所以它会被延迟,所以for循环将永远存在。

更新不使用IIFE

var secondMethod = function (someStuff) {
    var promise = new Promise(function (resolve, reject) {
        let randomNumber;
        function doItUntilGetEnoughBigNumber() {
            setTimeout(function () {
                randomNumber = Math.random();
                if (randomNumber > .9) {
                    console.log("got a number big enough", randomNumber);
                    resolve();
                } else {
                    console.log('the number wasn\'t big enough', randomNumber);
                    doItUntilGetEnoughBigNumber();
                }
            }, 1000)
        }
        doItUntilGetEnoughBigNumber();
    });
    return promise;
};

【讨论】:

  • 像魅力一样工作,非常感谢。 :) 一个后续问题,如果可以的话:您将 doItUntilGetEnoughBigNumber 函数包含在括号内,并在函数后面加上开/关括号。我不习惯这种语法,它看起来对我来说是多余的,但如果我删除它们就足够了。他们提供什么功能?
  • 不客气!这称为 IIFE(立即调用函数表达式)developer.mozilla.org/en-US/docs/Glossary/IIFE 当然,您可以通过删除括号并显式调用函数来做到这一点。
  • 我在不使用 IIFE 的情况下添加了新版本
【解决方案2】:

关于异步事件(例如,当 setTimeout 完成时)的重要一点是,处理程序不是直接调用的,而是将事件放入所谓的事件队列中。 javascript 引擎(事件循环)从其中取出一个又一个事件并执行它,当它完成后它会取出下一个事件。首先,它将执行您的 for 循环并设置所有超时。然后当它完成并经过一秒钟后,它将开始执行 setTimeout 处理程序。但由于你的 for 循环是无限的,它永远不会到达这一步。


最简单的方法是使用async 语法:

  const timer = ms => start => new Promise(res => setTimeout(res, ms));

  async function loop(){
    while(true){
      await timer(1000)();
      if(Math.random() > 0.9) return;
   }
 }

 timer(1000)()
    .then(loop)
    .then(timer(1000))
    .then(() => console.log("done"));

或者,您可以使用递归异步函数:

async function loop(){
  await timer(1000)();
  if(Math.random() > 0.9)
     return true;
  else
     return loop();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-23
    • 2017-12-10
    • 1970-01-01
    • 2018-10-13
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    相关资源
    最近更新 更多