【发布时间】:2017-10-20 10:57:46
【问题描述】:
我正在内存中实现一个数据结构 它掩盖了存储在网络上某处的大型数据结构的一部分。 假设有问题的数据结构是二叉树。 我希望内存树最初只包含根节点, 随着用户(或算法)的探索,它应该通过按需从网络上获取节点来惰性增长。
做到这一点的一种自然方法是为树节点数据类型提供方法getLeftChild()、getRightChild()、
每个都立即为各自的子节点返回一个 promise。
当 getLeftChild() 在其左子节点已在内存中的树节点上调用时,
它返回一个已经用缓存的孩子解决的承诺;
否则它会启动对孩子的获取(如果尚未由先前的调用启动)并为它返回一个承诺,
当获取的孩子最终从网络上回来时,获取的孩子将被保存在内存中以备将来使用并用于解决承诺。
所以,要打印左分支下 5 层的节点,我会说:
root.getLeftChild()
.then(child0 => child0.getLeftChild())
.then(child00 => child00.getLeftChild())
.then(child000 => child000.getLeftChild())
.then(child0000 => child0000.getLeftChild())
.then(child00000 => {
console.log("child00000 = ", child00000);
});
或(感谢@Thomas):
const lc = node => node.getLeftChild();
Promise.resolve(root)
.then(lc).then(lc).then(lc).then(lc).then(lc)
.then(child00000 => {
console.log("child00000 = ", child00000);
});
或者,同样的事情使用async/await:
(async()=>{
let child0 = await root.getLeftChild();
let child00 = await child0.getLeftChild();
let child000 = await child00.getLeftChild();
let child0000 = await child000.getLeftChild();
let child00000 = await child0000.getLeftChild();
console.log("child00000 = ",child00000);
})();
这一切都很好,调用代码在任何一种情况下都不会太糟糕。
我唯一的疑虑是,在二叉树(或任何类似的链接数据结构)中探索时 已经在内存中,我不想承受启动新微任务的开销 每次我想从内存数据结构中的一个节点到邻居时。 想象一个算法,其核心计算执行数百万次这样的链接跟踪操作。
Promises/A+ 对于每个then 回调执行确实需要一个新的微任务(至少):
2.2.4 在执行上下文堆栈仅包含平台代码之前,不得调用 onFulfilled 或 onRejected。 [3.1]。
我相信async/await 也有类似的要求。
我想知道的是:最简单/最干净的制作方法是什么
一个类似 Promise 的对象,其行为与 Promises/A+ 承诺完全相同,except 用于第 2.2.4 条?
IE。我希望它有一个“同步时可用”的then(或then-like)方法,以便上面的第一个代码sn-p
将在不产生执行上下文的情况下一次性执行。
为避免命名问题/混淆,我很乐意避免调用我的同步可用访问器 then
(由于 Promises/A+,这实际上是一个保留字);相反,我将其称为thenOrNow。
我将把我假设的类型/实现称为PromiseOrNow。
我是否必须从头开始编写 PromiseOrNow,或者是否有一种简洁可靠的方法来利用现有的 Promises/A+ 实现,例如原生 Promise?
请注意,因为我不打算弄乱任何名为“then”的东西,
PromiseOrNow 可能顺带符合 Promises/A+,如果结果证明这是一种好方法的话。
也许它是从原生Promise.prototype 中继承的原型。
这些属性在某些方面会很好,但它们不是必需的。
【问题讨论】:
-
关于你的 sn-p 的一件事,避免回调地狱:
root.getLeftChild().then(child0 => child0.getLeftChild()).then(child00 => child00.getLeftChild()).then(child000 => child000.getLeftChild()).then(child0000 => child0000.getLeftChild()).then(child00000 => { console.log("child00000 = ",child00000); });甚至更好,DRY:const lc = node => node.getLeftChild();然后Promise.resolve(root).then(lc).then(lc).then(lc).then(lc).then(lc).then(child00000 => { console.log("child00000 = ",child00000); }); -
@Thomas,谢谢!你的重写很棒。显然我仍然是一个有承诺的新手,否则我会注意到回调地狱。我会将这些编辑到问题中并归因于您。关于 async/await 版本的任何类似见解?
-
好的,我已经编辑了@Thomas 的改进。我看不出如何重写 await/async 版本,否则它会变成某种嵌套地狱,所以我将保持原样。
标签: javascript promise es6-promise