我正在内存中实现一个数据结构,它隐藏了存储在Web上某个地方的大型数据结构的一部分 . 假设所讨论的数据结构是二叉树 . 我希望内存中的树最初只包含根节点,并且应该像用户(或算法)探索的那样,通过从Web上按需获取节点来延迟增长 .
一种自然的方法是使树节点数据类型提供方法 getLeftChild()
,_ getRightChild()
,每个方法立即返回相应子节点的promise . 当在其左子节点已在内存中的树节点上调用 getLeftChild()
时,它将返回已使用缓存子节点解析的promise . 否则它会启动一个孩子的获取(如果尚未通过之前的调用启动)并返回一个承诺,当获取的孩子最终从Web返回时,获取的孩子将被保存在内存中以供将来使用解决这个承诺 .
因此,要在左侧分支下打印节点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
有类似的要求 .
除了第2.2.4节之外,我最简单/最干净的方法是制作类似Promise的对象,其行为与Promises / A的承诺完全相同?即我希望它有 then
(或 then
-like)方法"synchronous-when-available",这样上面的第一个代码片段将在一次性执行而不会产生执行上下文 .
为了避免命名问题/混淆,我很高兴不要调用我的同步可用访问器 then
(由于Promises / A,它现在实际上是一个保留字);相反,我会称之为 thenOrNow
. 我会打电话给我假设的类型/实现 PromiseOrNow
.
我是否必须从头开始编写 PromiseOrNow
,或者是否有一种利用现有的Promises / A实现(例如native Promise
)的简洁可靠的方法?
请注意,由于我不打算弄乱任何名为“ then
”的东西,所以 PromiseOrNow
偶然可能是Promises / A兼容,如果这是一个很好的方法 . 也许它将是原生的 Promise.prototype
原型 . 这些属性在某些方面会很好,但它们不是要求 .
3 回答
您可以使用以下包装函数使用
thenOrNow
方法扩展标准promise:您可以创建自己的myPromise构造函数,而不是使用包装器,但主要逻辑是相同的 .
关于立即解决的承诺
如果promise以异步方式解析(即在原始promise上调用
addThenOrNow
之后),thenOrNow
的上述实现将只能同步执行回调,就像您的情况一样(假设您的http请求是异步执行的) . 但是,如果promise立即(同步)解析,thenOrNow
将无法通过本机Promise接口同步获取值 .其他库,如
bluebird
为synchronous inspection提供了方法,因此如果包含bluebird
,您可以提供一个解决方案,该解决方案也适用于立即解决承诺:但同样,由于您的场景本质上是异步的(从HTTP请求获取响应),您可以使用任一解决方案 .
它们被称为"microtask",因为它们具有微观开销 . 微任务队列非常快,你不必担心 . 更好的keep consistency而不是为Zalgo而堕落 .
使用具有此功能的现有实现 . 例如,Q v0.6 contained an asap method .
不,您可以从适合您的Promise库开始 .
不,或者至少不是来自其公共API,因为它不具有同步检查功能 .
抱歉延迟,但我很忙 . 如何通过不同的方法来解决实际问题?而不是试图以同步的方式解决Promise,刮掉几毫秒,如何分离任务的同步和异步部分 .
确切地说:此处的异步部分是加载二叉树中特定节点的数据 . 即使树是懒惰的,颠倒也不一定是异步的 .
因此,我们可以从异步数据加载中分离出树的转换和懒惰代 .
到目前为止,一切都是(虽然是懒惰的)老式的同步代码 . 现在是异步部分,访问节点的数据 .
实施:
还有一个基本的例子
我不擅长编写例子 . 与课程一起玩,并根据需要修改/扩展它