-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't we need Events/Observer Pattern first? #111
Comments
+1 I would be sad if we saw ECMA standardized signals before a lower-level observer primitive. I'd like to see dependency tracking and glitch-free propagation guarantees handled at the observer layer rather than the signals layer. |
I would be happy with either a lower-level building block here, or even a standardized, symbol-based event protocol that could sit on top of For example, a bare-bones event protocol might look something like: interface Evented {
[Symbol.listen](eventKey: unknown, handler: (...args: any[]) => void): Disposable;
} Where a EventTarget.prototype[Symbol.listen] = function (eventKey, handler) {
this.addEventListener(eventKey, handler);
return { [Symbol.dispose]: () => { this.removeEventListener(eventKey, handler); } };
}; Alternatively, if function identity-based registration is preferred, we could have a simple protocol like this: interface Evented {
[Symbol.listen](eventKey, handler): void;
[Symbol.unlisten](eventKey, handler): void;
[Symbol.notify](eventKey, ...args): void;
} It's more consistent with Several years ago I sketched a design for protocol-based events + syntax like so: class Button {
event click;
event mousedown;
event mouseup;
#handleClick(m) {
this::click({ x: m.x, y: m.y }); // looks up an event key and invokes it's handlers
}
}
const btn = new Button();
const handler = e => { };
btn::click += handler;
btn::click -= handler; which might be transposed into something like: class Button {
#events = new Map([
["click", new Set()],
["mousedown", new Set()],
["mouseup", new Set()]
]);
[Symbol.notify](eventKey, ...args) { ... }
[Symbol.listen](eventKey, handler) { ... }
[Symbol.unlisten](eventKey, handler) { ... }
#handleClick(m) {
this[Symbol.notify]("click", { x: m.x, y: m.y });
}
}
const btn = new Button();
const handler = e => { };
btn[Symbol.listen]("click", handler);
btn[Symbol.unlisten]("click", handler); I used function window::onload(e) {} to attach events. |
I would be interested in something like a generic performant "not DOM related EventTarget". Why? As clients has multiple contracts and a single user can have multipel contracts we end up with about 400k "EventListener" on those Objects. We tried using Summary: using native EventTarget: ~25 seconds loading time I would have assume the exact opposite - but it seems EventTarget is doing a lot more then just registering and dispatching Events... => so I assume a native "basic" EventTarget would be even faster then our custom implementation |
+1 class EventSignal {
connect(...);
disconnect(...);
dispose();
emit(...);
}
class WriteableSignal {
connect(...);
disconnect(...);
dispose();
get();
set();
mutate(...); // should call an fn and after is notify all listener. should be use to update a complex state.
peek();
asReadonly();
}
class ReadonlySignal {
connect(...);
disconnect(...);
dispose();
get();
peek();
} This would enhance the usability and flexibility of the Signal class. |
I think the semantics for signals differ somewhat from that of a classical events/observer pattern. Signals need to be pulled to "do" anything while events are usually pushed. While they could use the same structural interface it may even be confusing for the user since usually interfaces and naming conventions set expectations around their behavior. |
The place where an event/observer pattern comes up with Signals is in the Watcher API. It would be great to follow some broader pattern if it can satisfy the following requirements:
Overall, the Observer pattern might be a relatively good fit (as @alxhub proposed several months ago), especially if we permit ourselves the tweak of using the Computed signals directly as the events, rather than wrapping them an extra time. I don't think Events or Observables would meet some of these efficiency goals, especially when it comes to batching or memory allocation. Computed and State signals do not follow any sort of event/observable/observer pattern, since they are not about explicit subscription/disposal or triggering at particular times. They are generally intended to be used for data dependency graphs. Using them in an event/observable fashion quickly leads to the classic "glitch" patterns and is somehow "push-based". The graph itself has to be based around a "pull-based" organizing principle for it to work. |
The core functionality of Signals depend on the general principle of the Observer Pattern/Events. Adopting Signals before a standard event mechanism feels like putting the cart before the horse.
IMO, the only reason we don't have a standard event pub/sub mechanism in ECMA262 is that there already is one in the DOM (
EventTarget
) and NodeJS (EventEmitter
). But the lack of a common, general purpose event system has led to situations likeAbortSignal
dragging alongEventTarget
's complexity along with it to every non-DOM implementation. We continue to tack on new built-in and host-provided functionality that could be served by a common event system and end up with numerous, disparate ways of handling the same underlying mechanisms:EventTarget
(addEventListener
/removeEventListener
/raiseEvent
using function identity)EventEmitter
(on
/off
/fire
using function identity)FinalizationRegistry
(regisister
/unregister
with a token)AbortSignal
(usesEventTarget
but also has privileged subscriptions for DOM built-ins,abort
)Promise
(then
,resolve
,reject
)Observable
(either as proposed to ES or WHATWG,subscribe
/unsubscribe
)yield
,next(x)
)setTimeout
/clearTimeout
et al (numeric handle)Signal
/Watcher
(watch
/unwatch
with signal object identity)I'd prefer to see some sort of observer pattern/events mechanism in the core specification before something more advanced like Signals.
The text was updated successfully, but these errors were encountered: