Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.

JS: dealing with events

Regis Kuckaertz edited this page Feb 11, 2019 · 1 revision

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.

Clone this wiki locally