【问题标题】:Promise with recursion递归承诺
【发布时间】:2018-03-27 23:28:19
【问题描述】:

我查看了一些关于 Promise 中递归的问题,但对如何正确实现它们感到困惑:

我整理了一个简单的示例(见下文) - 这只是一个示例,因此我可以了解如何使用 Promise 进行递归,而不是我正在工作的代码的表示。

Net-net,我希望承诺解决,但根据节点上的输出,它没有解决。对如何解决这个问题有任何见解吗?

var i = 0;

var countToTen = function() { 
    return new Promise(function(resolve, reject) {
        if (i < 10) {
            i++;
            console.log("i is now: " + i);
            return countToTen();
        }
        else {
            resolve(i);
        }
    });
}

countToTen().then(console.log("i ended up at: " + i));

以及控制台上的输出:

> countToTen().then(console.log("i ended up at: " + i));
i is now: 1
i is now: 2
i is now: 3
i is now: 4
i is now: 5
i is now: 6
i is now: 7
i is now: 8
i is now: 9
i is now: 10
i ended up at: 10
Promise { <pending> }

承诺永远不会解决。

【问题讨论】:

  • return countToTen(); 更改为 resolve(countToTen()); - 控制台仍会显示“待处理”,但是,如果您改为使用 var p = countToten......,那么您会看到 p 已解决
  • 你遇到的另一个问题是你的.then 是错误的......然后需要一个 function 作为参数,你提供了undefined (结果运行 console.log 作为参数 - 这就是为什么控制台的输出对你来说看起来不错
  • 谢谢你@Jaromanda X,因为你没有写答案所以投了赞成票,否则会接受你的。感谢您的回复。
  • 是的,不知道为什么我应该回答时发表评论:p

标签: javascript recursion promise


【解决方案1】:

如果您查看您的代码,只要i 小于 10,您就是在递归并且永远不会解决承诺。你最终解决了一个承诺。但这不是最初的调用者得到的承诺。

您需要使用递归返回的承诺来解决。如果您使用 promise 进行解析,系统将如何工作,直到值也被解析后才会解析:

let i = 0;
const countToTen = () => new Promise((resolve, reject) => {
    if (i < 10) {
      i++;
      console.log("i is now: " + i);
      resolve(countToTen());
    } else {
      resolve(i);
    }
  });

countToTen().then(() => console.log("i ended up at: " + i));

最后一部分也有错误。你没有为then 提供函数,所以如果你做了一些实际上会等待的事情,你会首先得到"i ended up at: 0"

【讨论】:

  • 您可能还想指出 .then 的错误用法 - 因为您在答案中也更改了该代码:p - 因为如果 .then 使用正确,OP 将看不到最终输出
  • 非常感谢它真的帮助了我!我要问一个新问题....及时找到了这个答案:-)真的很高兴.....
【解决方案2】:

如果你将i作为函数的参数而不是依赖外部状态会更好

const countToTen = (i = 0) =>
  new Promise ((resolve, _) =>
    i < 10
      ? (console.log (i), resolve (countToTen (i + 1)))
      : resolve (i))
      
      
countToTen () .then (console.log, console.error)
// 0 1 2 3 4 5 6 7 8 9 10

如果你也将10 设为参数会更好

const countTo = (to, from = 0) =>
  new Promise ((resolve, _) =>
    from < to
      ? (console.log (from), resolve (countTo (to, from + 1)))
      : resolve (from))

countTo (7, 2) .then (console.log, console.error)
// 2 3 4 5 6 7

更通用的方法是 reverse 折叠 - 或 unfold

const unfold = (f, init) =>
  f ( (x, acc) => [ x, ...unfold (f, acc) ]
    , () => []
    , init
    )

const countTo = (to, from = 0) =>
  unfold
    ( (next, done, acc) =>
        acc <= to
          ? next (acc, acc + 1)
          : done ()
    , from
    )

console.log (countTo (10))
// [ 0, 1, 2, 3, 4, 5, 6,  7, 8, 9, 10 ]

console.log (countTo (7, 2))
// [ 2, 3, 4, 5, 6, 7 ]

但你想要一个异步展开,asyncUnfold。现在用户提供的函数 f 可以是异步的,我们得到所有收集值的 Promise

const asyncUnfold = async (f, init) =>
  f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
    , async () => []
    , init
    )

const delay = (x, ms = 50) =>
  new Promise (r => setTimeout (r, ms, x))

const countTo = (to, from = 0) =>
  asyncUnfold
    ( async (next, done, acc) =>
        acc <= to
          ? next (await delay (acc), await delay (acc + 1))
          : done ()
    , from
    )

countTo (10) .then (console.log, console.error)
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

countTo (7, 2) .then (console.log, console.error)
// [ 2, 3, 4, 5, 6, 7 ]

这是一个更实际的例子,我们有一个记录数据库,我们希望执行递归查找或其他操作......

  • db.getChildren 接受节点 id 并仅返回节点的立即子节点

  • traverse 接受节点 id 并递归获取所有后代子节点(深度优先顺序)

const data =
  { 0 : [ 1, 2, 3 ]
  , 1 : [ 11, 12, 13 ]
  , 2 : [ 21, 22, 23 ]
  , 3 : [ 31, 32, 33 ]
  , 11 : [ 111, 112, 113 ]
  , 33 : [ 333 ]
  , 333 : [ 3333 ]
  }

const db =
  { getChildren : (id) =>
      delay (data [id] || [])
  }

const Empty =
  Symbol ()

const traverse = (id) =>
  asyncUnfold
    ( async (next, done, [ id = Empty, ...rest ]) =>
        id === Empty
          ? done ()
          : next (id, [ ...await db.getChildren (id), ...rest ])
    , [ id ]
    )

traverse (0) .then (console.log, console.error)
// [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]

【讨论】:

    【解决方案3】:

    尽量不要在你的函数中使用共享的可变状态(尤其是当它们是异步的时候)。您正在使用 window.i 但任何东西都可以更改该值,这不是必需的,因为 i 值仅在您的函数中用作计数器:

    const later = (milliseconds,value) =>
      new Promise(
        resolve=>
          setTimeout(
            ()=>resolve(value),
            milliseconds
          )
      );
    
    const countTo = toWhat => {
      const recur = counter =>
        later(1000,counter)
        .then(
          i=>{
            console.log(`i is now: ${i}`);
            return (i<toWhat)
              ? recur(i+1)
              : i;
          }
        )
      return recur(1);
    }
    
    countTo(10)
    .then(
      i=>console.log(`i ended up at: ${i}`)
    );

    【讨论】:

    • 谢谢。这只是一个例子。 “我整理了一个简单的例子(见下文)——这只是一个例子,所以我可以理解如何使用 promises 进行递归,而不是我正在工作的代码的表示。”
    【解决方案4】:

    很多成员已经提到,需要用递归返回的promise来解决。

    我想将代码共享为async/await 语法。

    const printNumber = (i) => console.log("i is now: " + i);
    
    // recursive function to call number increment order
    const recursiveCallNumber = async (i, checkCondition) => {
        // if false return it, other wise continue to next step
        if (!checkCondition(i)) return;
        // then print
        printNumber(i); 
         // then call again for print next number
        recursiveCallNumber(++i, checkCondition);
    }
    
    await recursiveCallNumber(1, (i) => i <= 10);
    

    【讨论】:

      猜你喜欢
      • 2014-02-04
      • 2018-05-29
      • 2017-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-23
      • 2017-09-21
      相关资源
      最近更新 更多