For AI agents: the complete documentation index is available at /tc39-atlas/en/llms.txt, the full documentation bundle is available at /tc39-atlas/en/llms-full.txt, and this page is available as Markdown at /tc39-atlas/en/proposals/year/pending/proposal-emitter.md.
  • English
  • Emitter S1

    Proposal details
    Proposal overview

    This proposal introduces a general-purpose container called Emitter for handling sync and async, single and multiple values, unifying APIs like EventTarget and EventEmitter. It provides composable operators (e.g., map, filter, reduce, flatten) for algorithmic transformations, aiming for high performance. The proposal includes examples and an API outline, but its stage/status is not mentioned in the provided text.

    Note

    The README below comes from the upstream repository and may contain outdated stage or status metadata. Use the proposal details above as the current source of truth.

    ECMAScript proposal: Emitter

    • JavaScript's most general container, over single and multiple, sync and async values
    • Operators form the Generics, a large part of the eventual standard library
    • Composable algorithmic transformations, decoupled from their input and outputs
    • Well-integrated with language rather than inventing lots of new concepts, (i.e. Promises, iteration, async iteration, etc)
    • Backwards-compatible unification of platform API (interfaces like EventTarget and EventEmitter)
    • Ultra-High Performance (faster than most.js) and memory efficient

    Reactive programming examples

    Log XY coordinates of click events on <button> elements:

    const { on, map, filter, run } = Emitter
    
    run(
      on(document, 'click')
    , filter(ev => ev.target.tagName === 'BUTTON')
    , map(ev => ({ x: ev.clientX, y: ev.clientY }))
    , coords => console.log(coords)
    )

    Map clicks from two buttons to a stream of +1 and -1, merge them and render the result:

    const { on, map, tap, reduce, flatten, render } = Emitter
    
    const inc = map(() => 1, on(buttonA, 'click')) 
    const dec = map(() => -1, on(buttonB, 'click'))
    const counter = reduce(0, flatten(inc, dec))
    const render = tap(total => { output.innerText = String(total) }, counter)
    run(render)

    Fork 10 peers, wait till they've all connected, return a reference to the processes:

    const { run, until, once, flatten, reduce } = Emitter
    const { fork } = require('child_process')
    
    const processes = await run(
      10
    , map(() => fork('peer.js'))
    , map(async peer => {
        await until(d => d == 'connected', on(peer, 'message'))
        return peer
      })
    , flatten()
    , reduce([])
    )

    Iterable programming examples

    From a range of numbers, divide them by 4, remove any greater than 12, then start collecting those one by one:

    function* range(from, to) { 
      for (let i = from; i <= to; i++) yield i  
    }
    
    const arr = run(
      range(40, 60)
    , map(d => d / 4)
    , filter(d => d < 12)
    , reduce([])
    )

    Transform a series of numbers one by one and log the result:

    for (const d of map(d => d + 1, [1, 2, 3]))
      console.log('transformed output', d)

    Log whenever the user presses CTRL+C

    for await (const d of filter(([n]) => n == 3, process.stdin))
      console.log('SIGINT!')

    API

    Transformation

    Filtering

    Running

    Creating


    Background

    For general background into this space, especially from more of a language perspective, Rich Hickey's talk on the motivation, design and use of Clojure's core.async library is a must.

    For comparison, here would be Rob Pike's original example of reducing tail latency using replicated search servers discussed in the talk:

    const { race, timeout, until, reduce, flatten } = Emitter 
    
    const results = await race(
      timeout(80)
    , until(3, reduce([], flatten(
        race(web1(query), web2(query))
      , race(img1(query), img2(query))
      , race(vid1(query), vid2(query))
      )))
    )

    It's useful to also familiarise with existing JavaScript libraries in this space:

    More details are included and will be documented in the FAQ.