-
Notifications
You must be signed in to change notification settings - Fork 0
JS: dealing with events
The atom library provides an alternative model for dealing with events. Instead of using the imperative API directly, events producers/consumers leverage a data structure called Channel, which is used to handle communication between the two. To start listening for an event, we can define a channel that will produce those events using fromEvent(eventName: string, eventTarget: EventTarget):
import type { Channel } from 'channels';
import { fromEvent } from 'events-getter';
const myButton: HTMLElement = ???;
const clickChan: Channel<Event> = fromEvent('click', myButton);The event listener will be automatically removed once the channel is closed:
clickChan.close();In order to access events, one has to go through the channel API. This is detailed below:
Channel<A>.map(f: A => B) : Channel<B>
With map, one can transform the original value of type A into a value of type B that can be processed downstream. Of course, multiple calls to map can be chained together:
clickChan.map(e => e.target) : Channel<HTMLElement>Channel<A>.filter(p: A => boolean) : Channel<A>
Lets through only those values of type A which satisfy the predicate p.
clickChan.filter(e => e.positionX > 500)Channel<A>.merge(ch: Channel<A>) : Channel<A>
Merges two streams producing the same type of values together. This is handy for when multiple event sources lead to the same outcome, e.g.
const kbdChan: Channel<Event> = fromEvent('keypressed', document).filter(e => e.key === 'q')
clickChan.merge(kbdChan) : Channel<Event>Channel<A>.dropRepeats(f: ([A,A]) => boolean) : Channel<A>
As it says on the tin. When two or more consecutive values are identical, only the first one is passed through.
Channel<A>.takeN(n: number) : Channel<A>
Creates a channel that only produces the first n values and then closes. This is useful for dealing with events that involve some level of transformation and can only ever be caught once:
const audioElement : HTMLAudioElement = ???;
fromEvent('timeupdate', audioElement)
.filter(e => audioElement.currentTime / audioElement.duration >= .25)
.takeN(1)
.tap(e => alert('25% of the audio played!');Channel<A>.dropN(n: number) : Channel<A>
Creates a channel that only starts producing after the first n values (they will be dropped).
Channel<A>.tap(f: A => void) : void
Registers a consumer for values produced by the channel. Note that channels are unicast, only one consumer can be registered.