question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Removing Kefir.emitter()

See original GitHub issue

TLDR; (For people who came here from “How do I create Emitter/Subject/Bus in Kefir?” questions)

Kefir once had Kefir.emitter() that had both Stream and Emitter interfaces. Then it was removed, because:

  • We prefer to do things in declarative/functional style rather than imperative in Kefir and FRP
  • Kefir.emitter() is an imperative pattern
  • In 90% of cases Kefir.emitter() can be replaced with some declarative/functional style pattern (you can find some examples bellow in this thread)
  • Lack of Kefir.emitter() should inspire people to find that better patterns
  • In remaining 10% cases one can extract emitter from Kefir.stream(emitter => ...) (see https://github.com/rpominov/kefir/issues/88#issuecomment-92512136)

Also note that Kefir.fromBinder() mentioned often bellow is renamed to Kefir.stream() now.

If you have more questions, please continue discussion here rather that where from you have been redirected to this thread. So we have all answers in one place.

Below goes original issue body:


Kefir.emitter() looks like the most basic method for creating general purpose streams, but I think it actually an antipattern. Beginners tend to overuse it, but people who get FRP right, use it very rarely or even never.

I think we should remove (deprecate) Kefir.emitter(), and advertise Kefir.fromBinder() as the main method for creating general purpose streams. And we should probably rename Kefir.fromBinder() to something looking less scary and more like a main method for creating streams — Kefir.stream() or something. Also Kefir.bus() should be removed, as it mostly same as Kefir.emitter().

This proposition also aligns well with idea of minimizing API surface area https://github.com/pozadi/kefir/issues/71


Here is the issues one can run into when using Kefir.emitter():

  1. Emitting before anyone listening:
var emitter = Kefir.emitter();
emitter.emit(1);

// The callback obviously won't be called with `1`, but sometimes it
// not so obvious for some people.
emitter.onValue(function(x) {
  console.log(x);
});


// Right way to achieve this
var stream = Kefir.fromBinder(function(emitter) {
  emitter.emit(1);
});

// will log `1`
stream.onValue(function(x) {
  console.log(x);
});
  1. Resources are not managed correctly:
// bad
function getStream() {
  var emitter = Kefir.emitter();
  // subscriber will never be removed
  target.addEventListener('click', function(event) {
    emitter.emit(event);
  });
}

// good
function getStream() {
  return Kefir.fromBinder(function(emitter) {
    target.addEventListener('click', emitter.emit);
    return function() {
      // free resources
      target.removeEventListener('click', emitter.emit);
    }
  });
}
  1. A property not getting values from an emitter while not active (see http://pozadi.github.io/kefir/#active-state)

  2. Methods for emitting events are exposed for all users of emitter-srteam:

var stream = getStream(); // using function from example above

// no one stops me to doing:
stream.emit(1);

And I guess there is more.

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:32 (20 by maintainers)

github_iconTop GitHub Comments

2reactions
irskepcommented, Jun 1, 2020

I came across this issue while working on a Typescript code base and thought I’d drop in a type-safe implementation for future google-users. It’s just a slightly tweaked version of the JS version above.

I needed this because I’m introducing FRP into a codebase that didn’t initially have it, and I don’t want to have to refactor the whole thing at once.

import * as kefir from "kefir";

export default class KefirBus<T, S> {
  private pool: kefir.Pool<T, S>;

  private emitter?: kefir.Emitter<T, S>;

  stream: kefir.Stream<T, S>;

  constructor(name: string) {
    this.pool = kefir.pool<T, S>();

    this.stream = kefir.stream((_emitter) => {
      this.emitter = _emitter;
      this.pool.onAny(_emitter.emitEvent);
      return () => {
        this.emitter = undefined;
        this.pool.offAny(_emitter.emitEvent);
      };
    });

    this.stream.setName(name);
  }

  emit(x: T): KefirBus<T, S> {
    this.emitter?.emit(x);
    return this;
  }

  error(x: S): KefirBus<T, S> {
    this.emitter?.error(x);
    return this;
  }

  end(): KefirBus<T, S> {
    this.emitter?.end();
    return this;
  }

  emitEvent(x: kefir.Event<T, S>): KefirBus<T, S> {
    this.emitter?.emitEvent(x);
    return this;
  }

  plug(x: kefir.Stream<T, S>): KefirBus<T, S> {
    this.pool.plug(x);
    return this;
  }

  unplug(x: kefir.Stream<T, S>): KefirBus<T, S> {
    this.pool.unplug(x);
    return this;
  }
}
1reaction
frankandrobotcommented, Jun 2, 2016

@rpominov Yea I’m not sure if this helps. By using a property, you trade “missing events” to “getting the wrong event”…which is just as potentially bad and hard to debug.

Thinking about this a little more, is the real issue that FRP isn’t designed for dynamically created listeners? It seems that the correct pattern should be “setup your listeners, then do stuff”. At least when analyzing the first and second gotchas (at the start of this thread), violating this pattern causes the issues. The same is true even if we use a pool (as shown above).

On a related note, I just finished reading this reddit and that guy seconded my notion that Buses were invented for exactly a use case like ReactJS.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to Remove Milk Kefir Grains From Finished Kefir
3 WAYS TO REMOVE GRAINS FROM FINISHED MILK KEFIR · Use Your (Clean!) Fingers · Use a Plastic Mesh Strainer · Pour Kefir...
Read more >
Kefir.js 3.8.5 (changelog)
Creates a stream that calls the given handler function, with the given interval in milliseconds. The handler function is called with one argument...
Read more >
pozadi/kefir - Gitter
emitter, and can't figure-out how to do this when I want to emit value from callback. I'm using React, and want to emit...
Read more >
kefir | Yarn - Package Manager
Kefir. Kefir — is an Reactive Programming library for JavaScript inspired by Bacon.js and RxJS with focus on high performance and low memory...
Read more >
JavaScript fromEvents Examples, kefir.fromEvents JavaScript ...
These are the top rated real world JavaScript examples of kefir. ... function streamAndEmitF () { let emitter = new EventEmitter() let emitF...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found