>That you conflate “async IO” and promises may be why you’re in this hole in the first place.
It's all just single threaded cooperative concurrency with context switching at IO. The isomorphic apis on top of this whether it's callbacks, async/await or promises is irrelevant to the topic at hand.
>I’m still really confused why a callback-hell topographical sort and process would be somehow better than a cache, a lock, and a breadth-first search—not least because it’s easier to follow and is also, anecdotally, faster—but clearly these mysteries are just plain beyond my pay grade.
I'm confused as to what the hell you're talking about. "callback-hell topographical sort and process" Wtf is that? Where were callbacks used in my example? Where was sort used?
Do you not understand that the dependencies determine the order of construction? That's it, it doesn't matter what technique you use the overall steps are the same. There is no bfs or callback hell going on. You manually instantiate the dependencies and choose what's async and what is sync. No need for locks.
Are you talking about something that takes a dependency graph and constructs the instance from that? If you want to do that your algorithm is incorrect. You need Post Order DFS, BFS won't work, but both BFS and DFS are O(N) so in terms of traversal over dependencies it's all the same.
class Node:
def __init__(self, createAnObject: AwaitableFunction[Any...], dependencies: List[Node])
self.deps = dependencies
self.constructor = createAnObject
async def constructObjectFromDependencyTree(root: Node) -> Any:
if root is None:
return None
else:
instantiatedDeps = await runAsync([constructObjectFromDependencyTree(node) for node in root.dependencies])
return await root.constructor(*[i for i in instantiatedDeps if i is not None])
The algorithm is bounded by O(N) where N is the amount of total dependencies.
If you want to construct an object with a total of N dependencies then no matter how you do it, the operation will ALSO be bounded by O(N). In terms of speed, it's all the same, but the above is how you're suppose to do it.
The above algorithm should give you what you want while providing concurrency and sequential execution exactly where needed. No callback hell, no promises, no sorting, no external shared state and no locks.
Regardless, if you're building Objects that necessitate such algorithms you are creating technical debt by creating things with long chains of dependencies. You should not be using your primitives to create large dependency trees; instead you should be composing your primitives into pipelines.
Additionally, relegating so much complexity to runtime is a code smell. If there aren't too many permutations bring it down to a manual construction with your code rather than an algorithm/framework.
It's all just single threaded cooperative concurrency with context switching at IO. The isomorphic apis on top of this whether it's callbacks, async/await or promises is irrelevant to the topic at hand.
>I’m still really confused why a callback-hell topographical sort and process would be somehow better than a cache, a lock, and a breadth-first search—not least because it’s easier to follow and is also, anecdotally, faster—but clearly these mysteries are just plain beyond my pay grade.
I'm confused as to what the hell you're talking about. "callback-hell topographical sort and process" Wtf is that? Where were callbacks used in my example? Where was sort used?
Do you not understand that the dependencies determine the order of construction? That's it, it doesn't matter what technique you use the overall steps are the same. There is no bfs or callback hell going on. You manually instantiate the dependencies and choose what's async and what is sync. No need for locks.
Are you talking about something that takes a dependency graph and constructs the instance from that? If you want to do that your algorithm is incorrect. You need Post Order DFS, BFS won't work, but both BFS and DFS are O(N) so in terms of traversal over dependencies it's all the same.
The algorithm is bounded by O(N) where N is the amount of total dependencies. If you want to construct an object with a total of N dependencies then no matter how you do it, the operation will ALSO be bounded by O(N). In terms of speed, it's all the same, but the above is how you're suppose to do it.The above algorithm should give you what you want while providing concurrency and sequential execution exactly where needed. No callback hell, no promises, no sorting, no external shared state and no locks.
Regardless, if you're building Objects that necessitate such algorithms you are creating technical debt by creating things with long chains of dependencies. You should not be using your primitives to create large dependency trees; instead you should be composing your primitives into pipelines.
Additionally, relegating so much complexity to runtime is a code smell. If there aren't too many permutations bring it down to a manual construction with your code rather than an algorithm/framework.