【问题标题】:Promise not resolving from recursive function with setTimeOut承诺不使用 setTimeOut 从递归函数中解析
【发布时间】:2019-01-23 03:28:32
【问题描述】:

我想在循环中经过一定的延迟后执行一些代码。一旦所有的迭代都完成了,我想做一些其他的任务。这些取决于从 task1 获得的结果。为此,我使用生成器、async/await 和 promise 编写了以下代码 sn-p:

function* iter() {
 for (var i = 0; i < 10; i++) yield i
}

async function start() {
    var myIter = iter();
    var p = await cb1(myIter);
    console.log('after await');
        p.then((value) => {
        console.log('-----------here-------------');
    });
}

start();

function cb1(myIter) {
    console.log("Started : " + new Date());
    var obj;
    return new Promise((resolve, reject) => {
        setTimeout(function(){
        if(myIter.next().done === true) {
            console.log("End : " + new Date());
            resolve('done');
        }else {
            console.log("---in else---");
            cb1(myIter);
        }
    }, 3000);
    });
}

问题是,p.then() 中的控制台永远不会被打印出来。这意味着承诺永远不会得到解决并且程序终止。迭代按预期执行,但承诺永远不会解决。这里有什么问题?我正在使用递归来触发 iterator.next() 并希望仅在最后一次迭代时解决承诺,即当 done=true 时。

很久以来就在这个问题上摸不着头脑。帮助表示赞赏。以下是该程序的输出。

【问题讨论】:

    标签: javascript recursion promise async-await generator


    【解决方案1】:

    几个问题:线

    var p = await cb1(myIter);
    

    导致p 被赋予调用cb1 的结果的p 不是 Promise 除非构造的Promise 解析Promise,这是不寻常的。因为await 实质上会暂停脚本的执行,直到promise 解决,所以您不需要.then - 您只需在start 函数中添加console.log。一旦你正确地将 Promise 链接在一起,p 将解析为'done'字符串,这当然不是Promise

    但是还有另一个问题:cb1 返回的承诺永远不会解决,除非在最后,你调用resolve。在else 中,您永远不会调用resolve,因此这些承诺永远无法解决。要解决此问题,请更改

      } else {
        console.log("---in else---");
        cb1(myIter);
      }
    

      } else {
        console.log("---in else---");
        cb1(myIter).then(resolve);
      }
    

    以便当前迭代的Promise下一个 迭代的Promise 解析后解析。

    function* iter() {
      for (var i = 0; i < 3; i++) yield i
    }
    
    async function start() {
      var myIter = iter();
      var p = await cb1(myIter);
      console.log('after await');
      console.log('-----------here-------------');
    }
    
    start();
    
    function cb1(myIter) {
      console.log("Started : " + new Date());
      var obj;
      return new Promise((resolve, reject) => {
        setTimeout(function() {
          if (myIter.next().done === true) {
            console.log("End : " + new Date());
            resolve('done');
          } else {
            console.log("---in else---");
            cb1(myIter).then(resolve);
          }
        }, 1000);
      });
    }

    【讨论】:

    • 这解决了我的问题。太感谢了。这很有趣,我学到了一些我不知道的东西。
    • 一如既往的好答案,但是:"p 不是Promise,除非构造的Promise 也解析为Promise,这是不寻常的。" i> 不正确。 p 永远不会是一个承诺。如果你将promise B 传递给promise A 的resolve,promise A 没有被解析,它被promise B 所奴役——它会根据promise B 的行为来解析或拒绝:jsfiddle.net/duj134qv
    • @T.J.Crowder 有人,最好是您或提出此答案的人应该警告 OP,您正在创建 n 个 Promises,其中 n 是迭代数并且正在向后解析。将 i 设置为 5000 左右并观察 ram 膨胀。这不是一个生产可行的代码,我相信你知道这一点。
    • @ibrahimtanyalcin - 嗯,这是CertainPerformance 的答案。我没有这样做,我认为额外的承诺没有意义。
    • 这样效率低吗?
    【解决方案2】:

    有几个问题:

    1. await 的结果永远不会是一个承诺; await 的目的是等待 promise 解决并给你分辨率值。所以你的代码中的p 不是一个承诺,也没有then 方法。但您不会因为 #2 而收到错误消息。
    2. 每次调用cb1 都会创建一个新的promise,并带有一个新的resolve 函数。您的最后一个 setTimeout 回调正在解决最后一个承诺,但没有任何东西可以解决第一个承诺,因此您永远不会超过 var p = await cb1(myIter); 行。

    您可能需要一个用于计时器回调的内部函数,然后让cb1 调用解析返回的承诺。

    类似的东西:

    function* iter() {
        for (var i = 0; i < 10; i++) {
            yield i;
        }
    }
    
    async function start() {
        var myIter = iter();
        var p = await cb1(myIter);
        console.log("p = ", p);
    }
    
    start();
    function cb1(myIter) {
        console.log("Started : " + new Date());
        return new Promise((resolve, reject) => {
            iteration();
            function iteration() {
                setTimeout(function() {
                    if (myIter.next().done) { // `=== done` is pointless here
                        console.log("End : " + new Date());
                        resolve('done');
                    } else {
                        console.log("---in else---");
                        iteration();
                    }
                }, 3000);
            }
        });
    }
    

    【讨论】:

    • 感谢您的回复
    【解决方案3】:

    还需要 1 个函数才能正常工作:

       function* iter() {
     for (var i = 0; i < 10; i++) yield i
    }
    
    async function start() {
        var myIter = iter();
        var p = await cb1(myIter);
        console.log('after await');
        console.log("here is p:" + p.done + "," + p.value);
    }
    
    start();
    
    function repeat(myIter,resolver,previous){
        var temp;
        if((temp = myIter.next()).done === true) {
            console.log("End : " + new Date());
            resolver(previous);
        }else {
            console.log("---in else---");
            setTimeout(function(){repeat(myIter,resolver,temp)},3000);
        }
    }
    
    function cb1(myIter) {
        console.log("Started : " + new Date());
        var obj;
        return new Promise((resolve, reject) => {
           repeat(myIter,resolve);
        });
    }
    

    同样修复了p,await把它取出来了,你需要获取之前的值

    【讨论】:

    • ok 修复了,不过他不应该等到完成,需要抓住以前的
    猜你喜欢
    • 1970-01-01
    • 2016-04-02
    • 2018-12-01
    • 1970-01-01
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 2019-05-25
    • 2019-04-15
    相关资源
    最近更新 更多