I tried to evaluate the "async transparency" of _hyperscript within event handlers...and failed. To my current knowledge of JS, it is principally impossible to implement it anyway:
stop(Immediate)Propagationare guaranteed to work at any time
stop(Immediate)Propagationwas called before)
haltcommand at the end of a _hyperscript event handler is therefore completely useless if the code executed before was actually async - but because of "async transparency" the user/programmer probably does not even know what kind of code he/she is running
I have made a small live example to illustrate the problem.
According to my research, in JS, async event handlers (which allow
stop(Immediate)Propagation at any time, even after async calls) are principally impossible
And even if this restriction can be circumvented somehow (e.g. by defining propagation/default handling prior to execution) one still has to implement a separate event queuing mechanism in order to keep the original order of incoming events intact - regardless of the duration of any async calls (this is what you tried, but according to my example, the order of events is not preserved)
Not preserving the order of events and/or letting several event handlers run in parallel opens the door for nasty race conditions making programming much more complicated than before. Particularly, as people may not recognize this danger just by simply "reading" _hyperscript code: "...It is very easy to read, making it obvious what a script is doing..." turns out not to be true any more...
Some (rough) ideas for potential solutions for the mentioned problem:
haltand let bubbling/default-handling be configured separately - perhaps depending on event filters (which must then be synchronous, though) If you are interested: here is how Svelte handles the behaviour of event handlers
passcommand (instead of
halt) that explicitly passes an event to any outer elements (you may have to implement this "custom bubbling" yourself and, perhaps, restrict it to triggering scripted event handlers only)
passcommand) - this could probably be implemented by cloning the original event (but now with option
false) and dispatching it again to the original
event.target(disadvantage: the original
event.targetwill get the event twice) Setting
falsewill keep the event away from any outer elements and prevent infinite loops
Perhaps, these ideas may help you saving _hyperscript as a whole - otherwise, the goals behind _hyperscript (e.g., "async transparency" and "It is very easy to read, making it obvious what a script is doing") will no longer be achieved...
In order to illustrate the potential fixes mentioned above, I've made two small live examples:
Meanwhile, I've written a third example which demonstrates a simple queue for (potentially) asynchronous events that avoids "race conditions"
Please note, that such a queue exists completely separate from a browser's built-in event handling - that's why it may be necessary to prevent DOM events from being handled by JS if there are (also) scripted handlers for them.
This solution is not ideal but it's the best I can think of right now...
halt doesn't necessarily stop the event handler, you can halt just the event bubbling or prevent default and continue execution:
so you can do this at the start of the script and then have whatever async behavior you want afterwards
if you have a conditional situation, where event propagation is dependent on an async result, the easiest thing would be to trigger a separate event based on that result and listen for that on the parent:
on click fetch /whatever as json if the result's whatever is true trigger it-was-true
I can imagine detecting when an event handler doesn't explicitly return from execution (which implies async behavior occurred), cancelling the event, and then, when it does finally return, cloning the event and re-dispatching it on the parent.
I have no doubts that people finally find ways to adjust their scripts in order to achieve the intended result
It is that "async transparency" implies that the programmer should not need to keep asynchrony in mind - because otherwise it's no longer "transparent".
I already wrote a number of JS programs myself in which I changed a previously synchronous approach to an asynchronous one (popular example:
localForage) - and actually had to apply an awful lot of changes as the move from "synchronous" to "asynchronous" changed so much...
Event handling with asynchronous handlers is especially difficult as the order of events (any events, not just those of the same
type) must be preserved - or the state machine you are implementing may no longer work as intended.
Again, if you continue to claim to be "async transparent", a script should work as intended regardless of whether certain commands are synchronous or not - the situation could even change over time - or depend on a configuration (again, you may use
localForage as a practical example)
We are talking about event bubbling here, right?
The hyperscript scripts themselves are async transparent (as best we can do) in that, if you switch from localStorage to localForage it shouldn't require any changes to your code unless you are relying on event bubbling ordering in some way.
I would recommend reading this:
Hyperscript isn't a formal language, it's a front end scripting language, and the goal is to be useful rather than semantically perfect. I get where you are coming from, and I appreciate that perspective, but we have our own on the matter. Event bubbling ordering isn't an area that is important enough to warrant additional implementation complexity, particularly given the simple work arounds available (trigger and listen for a different event).
If it comes up a bunch in real world use cases, we can take a look at handling it, but until then I'm wary of making abstract consistency the enemy of real world simplicity.
No, not just event bubbling (and default actions)
We are also talking about preserving the order in which events are handled in order to keep the behaviour of state machines predictable - in fact, that's the more important aspect...
Addendum: that's an interesting article you have linked to - the fundamental prerequisite, however, might be: "It is important to remember that the initial virus has to be basically good" - and here our opinions might diverge...
Yes, I see what you are saying. In general, hyperscript should behave as intended, but there are certainly cases where you do have to think a bit. We have the
and expression forms, for example, to handle specialized situations:
(frankly, they are a bit of a mess.)
And we have the
queue keyword to determine how event handlers behave when they receive events would imply parallel execution:
So, not perfect. But pretty good.
I don't want to discourage you from contributing, however, and I'm willing to continue to consider the issue. Maybe you'd like to jump on the discord, which is probably a better medium for back-and-forth speculative discussions than github?
As and aside, there is an area that I think would be very useful to contribute to hyperscript that might interest you: improving the debugger.
as mentioned somewhere else: I really like the idea of
and spent some time evaluating it (e.g., to see if I could build a visual IDE around it - s.th. like _hypercard). As you may see from the number of issues I opened, there were quite a number of situations that "surprised" me and consumed a lot of time...
For the time being, I'll have to put my activities on hold for a while and watch how _hyperscript evolves - perhaps experimenting with it again at some time in the future.
In any case, I wish you every success!
Addendum: just in case that I wasn't explicit enough: I am primarily interested in using _hyperscript, not so much in building it. Of course, I could enhance it here and there, if that does not consume too much time - but, actually, I prefer building applications with _hyperscript
|Issue Title||Created Date||Updated Date|