【问题标题】:JavaScript Promises: Recursively Building Promise Chain With Breadth-First TraversalJavaScript Promises:使用广度优先遍历递归构建 Promise 链
【发布时间】:2016-06-23 04:45:30
【问题描述】:

原生 Javascript ES5/ES6 承诺

我正在尝试导入具有递归关系的数据,因为 数据库(mongodb)正在分配 ids - 必须加载父级(异步) 在它的孩子可以加载之前(也是异步的)。

例如,此任务列表中的任务 B。

任务 A - 一些过程

任务 B - 递归异步加载(面包优先遍历)

任务 C - 依赖于任务 B

请注意,因为任务 C 在任务 B 完成之前无法启动,我假设 需要构建一个承诺链,在完成之前不会退出。

假设正在构建的链看起来像这样: (这棵树只有一个头)

promiseParent.then(Promise.all(childrenPromises.then(Promise.all(grandChildrenPromsies.then(....)))))

我想它会像广度优先队列一样遍历(最好我会 如果可能的话,想尽量避免使用队列数据结构)

我发现这个很难破解。任何建议或 解决方案?

【问题讨论】:

  • 你不把它锁起来吗? taskA.then(function() { return taskB() }).then(...
  • 任务 B 是挑战。任务 B 需要在调用任务 C 之前递归地构建一个要完成的承诺链。任务 A 无关紧要(仅显示在调用 B 之前发生的事情。@BlakesSeven
  • 我没有发表评论。 @adeneo 做到了。我所做的只是删除了 mongodb 标签(大概是由神奇的 stackexchange 问题编辑器自动插入的!),因为它确实与所有关于 promises 的问题没有具体关系。
  • 注意到但为时已晚,抱歉 Blake - 是的,我猜它与 Mongodb 社区不太相关。
  • 我的意思是,你不必嵌套它们,你可以把它们链起来

标签: javascript promise es6-promise


【解决方案1】:

Promise 链可以动态扩展,在链中的任何点插入新链接,只需从任何 .then 履行处理程序返回一个 Promise。

假设每个任务都使用其子级数组进行解析。如果子节点可以并行处理,那么:

promiseParent
.then(children => Promise.all(children.map(child => loadChild(child))))
.then(grandchildren => Promise.all(grandchildren.map(child => loadChild(child))))

应该这样做。如果必须按顺序处理子项,则:

let sequence = (a, f) => a.reduce((p, c) => p.then(() => f(c)), Promise.resolve());

promiseParent
.then(kids => sequence(kids, kid => loadChild(kid)).then(() => nextGen())
.then(gkds => sequence(gkds, kid => loadChild(kid)).then(() => nextGen())

会这样做(我通过假设 nextGen 知道返回下一代来简化)。

如果必须递归地发现子节点的数量,那么:

let loadChildrenRecursively = child => loadChild(child)
  .then(nextChild => nextChild && loadChildrenRecursively(nextChild));

promiseParent
.then(firstChild => loadChildrenRecursively(firstChild)).then(() => nextGen())
.then(firstGchild => loadChildrenRecursively(firstGchild)).then(() => nextGen())

应该这样做。

要将其推广到 N 个级别,请选择上述任何方法,例如 并行,然后对其进行递归:

let doGeneration = generation =>
  Promise.all(generation.map(child => loadChild(child))))
  .then(offsprings => offsprings && doGeneration(offsprings))

promiseParent.then(children => doGeneration(children));

因此,只要还有更多工作要做,您始终可以通过 resolving a promise with another promise 进行扩展(这是您通过从 .then 履行处理程序返回新承诺隐式执行的操作)。

【讨论】:

  • 关卡的数量似乎也是动态的。
  • @Bergi 我已经更新了答案以涵盖动态数量的级别。
  • @jib 神圣!!哇,这个答案看起来很漂亮!是的,我希望有一个动态的答案来允许分支深度的灵活性(所以感谢 Bergi 在其中添加它)和顺序,因为孩子在导入之前需要新的父 ID。
  • 谢谢!顺便说一句,in parallel 只是意味着与其他孩子并行,因此所有三个解决方案都可能可以访问父 ID,因为它们都等待所有父母在开始之前完成。 - 另外,如果父 id 是您唯一等待的东西,那么您可能不需要严格意义上的广度优先遍历,因为您不需要等待 兄弟姐妹 我不认为,只是你自己的父母?如果你想优化,那就是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-21
  • 2016-02-15
  • 1970-01-01
  • 2019-08-10
  • 1970-01-01
  • 2013-11-03
相关资源
最近更新 更多