Giter Site home page Giter Site logo

Comments (3)

postspectacular avatar postspectacular commented on May 19, 2024

Thanks, as always, @allforabit! You're right, that snippet is indeed a typo and it should be fx.ROUTE_TO. And I also hear you about this package direly needing some decent documentation, at least initially to just give a better overview of how these different parts fit together... Am currently deep diving at work and in my free time working out through different iterations devising guidelines for themeable components. Once that's more stable I will finally finish writing the 2nd mammoth blog post about this and probably can use parts of that for the interceptors readme too.

To briefly summarize the differences between event handlers & effects:

Event handlers are triggered by events, but each event handler is technically a chain of interceptors (even though many are just a single item). Even if you just specify a single function, it's internally translated into an array of interceptor objects like:

valueSetter("route") -> [{ pre: (...) => {[FX_STATE]: ...}, post: undefined }]

When processing an event, these interceptors are then executed first in ascending order for any pre functions and then backwards again for any post functions (only if there are any in the chain). So if you had defined an handler with this chain: [{pre: f1, post: f2}, {pre: f3}, {pre: f4, post: f5}], then the functions would be called in this order: f1, f3, f4, f5, f2. The post phase is largely intended for state/effect validation & logging post-update. Most interceptors I ever wrote are 95% using pre only.

[UNDOABLE_EVENT]: [snapshot(), valueSetter("foo")]

The idea of interceptors is quite similar to functional composition and AOP (aspect oriented programming). E.g. if I have multiple actions which should be undoable, I can compose my main event handlers with the snapShot() interceptor, which requires a @thi.ng/atom History-like instance and records a snapshot of the current app state, but else is completely invisible. So yes, like with trace() some interceptors DO have side effects (in fact these two are the only ones thus far), but they're really the exception from the rule and TBH not something I've ever worried about so far. Also snapshot() is idempotent since it only records a new snapshot if it's different from the last and trace() is usually only used during development and its side effect is outside the scope of your app (i.e. the console).

The idea of event handlers largely just being responsible to assign parameters to side effects, rather than executing effects themselves, is again mainly to do with the DRY-principle, instrumentation potential and performance. Most composed event handler chains are setup so that your "actual" main handler is last in line in the pre processing phase. If e.g. your event handlers would directly update the state atom, then any attached watches (derived views, cursors, other subscriptions) would be re-run each time. By assigning the updated state to FX_STATE we can avoid these interim updates and only apply the new state once all events in the current frame have been processed. Furthermore, a post interceptor might cancel the event due to validation errors etc.

Also in most apps there're many more event types/handlers than possible result actions. So assigning them to registered side effects gives good reuse. Another aspect is debugging potential. With a break point set at the beginning of processEffects() (in event-bus.ts) I can see exactly which side effects have been caused each frame. That alone saved me nerves countless times... Of course at some point there should be a widget/component for that purpose as well.. :)

As for not allowing undefined return values from interceptors: I'd say that's not necessary, but could be done if you think it helps with understanding. Though, would you want to force handlers to always require at least one side effect assignment? Else I could just return an empty object instead of undefined.

Btw. I was going to add another version of trace(), i.e. traceEffects() to log the current accumulated side effect assignments and for that reason considered renaming trace() to traceEvent(). Objections? I can't currently see how a trace side effect would work, also in terms of chronological ordering. My main use case for trace() so far was debugging the correct order of events fired (and re-fired/dispatched). For example in the app I'm currently working on there're dozens of high-level events whose only role is to re-dispatch several low-level events via FX_DISPATCH_NOW. trace() in its current form is very useful for that purpose.

from umbrella.

allforabit avatar allforabit commented on May 19, 2024

Thanks very much for this thorough explanation. It all makes a lot more sense now. In general, it's possible to get 99% of the way by reading the source code but this definitely fills in the final 1%! I can see now why the trace is setup like that. I don't think there's really a problem with it only in that it was a bit of a curveball when using the source code to learn how the system works. I know I can treat that (and snapshot) as the exception when it comes to interceptors. That sounds good about changing the name so that you can add more trace options. No need to worry about not allowing undefined I think. One thing I was experimenting with was setting up an FX_NOOP that could be returned when there's no side effects required although this might be overkill. Great tip on using the breakpoint at processEvents. I can imagine it coming in very useful at some point. Thanks again for all the info!

from umbrella.

postspectacular avatar postspectacular commented on May 19, 2024

Curveballs are never good and am grateful to you pointing them out! Don't know if you already saw it, but I started adding Architecture Decision Records for the hdom-components package and might add some for the interceptors package too. I find the format quite helpful to flesh out & document new ideas...

Btw. The side effect inspection via breakpoint in processEffects() is only a temporary until the traceEffects() has landed.

from umbrella.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.