Removing synchronous collection methods from the backend is Meteor’s biggest hurdle (I wonder how many apps have yet to migrate?).
Without the old Fibers-based “synchronous” APIs, we are missing one very grand thing in Meteor: Isomorphic reactive code
In lieu of this, it would be great if Meteor would gain new signals-and-effects patterns (reactivevar-and-tracker.autorun patterns) for handling async values.
The issue:
Promises are not repeatable, whereas signals are. This makes a world of difference in writing clean robust code: signals and effects allow for logic trees (including async state) to be easily cancelable and repeatable, but the removal of synchronous-like collection methods has caused Meteor developers to loose the ability to easily create robust (as in clean and error free) code that can easily be canceled and re-executed.
With single-valued Promises, we now have a landscape of new and inflexible code that is difficult to refactor to make cancelable and repeatable, risking race conditions and errors, which can only be solved with unweidly and error-prone try-catch blocks all over the place.
TODO give code examples.
Towards a solution:
If you’ve been keeping track of the frontend JS ecosystem, you may have noticed a thing called “signals and effects” have proliferated across frameworks (Preact, Vue, Svelte, Angular, Lit, etc, and even React with its fake version of it called Hooks (let’s not get into that)).
Meteor was king of this pattern, with ReactiveVar/Deps (signals) and Tracker.autorun (effects) existing before the signald and effects of all those other frameworks.
Signals and effects have gotten a lot of use lately, and people have come up with a lot of new patterns.
For example, “async signals” could mean different ways of implementing signals and effects, or implementing async handling on top of signals and effects.
I don’t have much time now, so I’ll leave two things here for now:
1) a contrived example of async code running two things in sequence, without using async/await (here using Solid.js API naming):
createEffect(() => { // Solid's "Tracker.autorun"
const [valueOne, oneDone] = processOne(input())
const [valueTwo, twoDone] = when(twoDone, () => processTwo(valueOne()))
const allDone = createMemo(() => oneDone() && twoDone())
createEffect(() => allDone() && console.log('done'))
})
which is equivalent to the following Promise-based (non-robust) example:
async function (input) {
const valueOne = await processOne(input)
const valueTwo = await processTwo(valueOne)
console.log('done')
})
The signals+effects code can sometimes be just a little bit more verbose than async/await, but the benefits are much more worth it.
I would like to write more on this topic (so far, it’s just been chats with people in the signals+effects community, namely Solid).
TODO more code examples.
2) A video that describes the robustness:
This video mentions “them” in reference to signals and effects for async management, it was a video I was giving someone in the context of a chat on the topic.
The video show an example written in a way similar to the contrived example above: sequences of async processes (in this case, sequences of animations) which can easily be executed in sequence, robustly canceled at any point in time, and repeated, without having to write unweildy error-prone try-catch/throw code as would be required with Promise/async/await, and without all the manual wiring and de-wiring of patterns like events, observables, pubsub, etc.
Now, I may need to give more code examples to fully explain this, but so far signals and effects allow writing the closest thing to declarative code for async processes (f.e. similar to CSS transitions and animations) that can easily be interrupted to let other process take over (in a similar way as how CSS transitions can be canceled/repeated/reversed simply by toggling a class name for example). Basically if one were to write the actual engine for something like CSS, this way of managing async code would be the best way to do it. Other patterns like Events, Observables, pubsub, and Promises, cannot achieve the same robustness in as clean of a manner in which all the pieces are composable while maintaining robustness.
If you know about how React Hooks greatly improved composition in React, well, same thing with signals and effects in Solid/etc (execept that signals+effects in Solid/etc are leaps ahead of React in at least a couple major ways: for one React Hooks are coupled to a component system and unusable on their own, and they do not have nesting hence no reactive robust cancelable/repeatable logic trees).
TODO more explanations and examples of these concepts, and how they are better than other patterns.