A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/tc39/proposal-observable/issues/201 below:

Simplification of Observable API · Issue #201 · tc39/proposal-observable · GitHub

Okay, I'm going to throw my hat back in and see if I can resurrect this a little.

What I'm going to propose is slightly different than the current proposal and different than RxJS, but I strongly feel it will work.

API
interface Observable<T> {
  new (
    initialization: (
      nextHandler: (value: T) => void,
      errorHandler: (err: any) => void,
      completeHandler: () => void, 
      signal: AbortSignal
     ) => void;
  ): Observable<T>

  subscribe(
      nextHandler?: (value: T) => void,
      errorHandler?: (err: any) => void,
      completeHandler?: () => void, 
      signal?: AbortSignal
  ): void;

  forEach(nextHandler: (value: T) => void, signal: AbortSignal): Promise<void>

  first(signal: AbortSignal): Promise<T>;

  last(signal: AbortSignal): Promise<T>;
}

The idea is to remove the need to define Observer and Subscriber, as in other Observable implementations, and use simple functions instead. Also using a cancellation token (ala AbortController/AbortSignal) instead of introducing a Subscription type.

I realize that AbortController and AbortSignal are not a part of JavaScript proper. However, I strongly feel JavaScript could use a cancellation primitive, and Observable, which is also a primitive, is not as useful without it.

Defining an observable instance

Below is the simplest use case for an observable. A synchronous set of values.

  test("should at least work", () => {
    const source = new Observable((next, error, complete, signal) => {
      next(1);
      next(2);
      next(3);
      complete();
    });

    let results = [];

    source.subscribe(value => results.push(value), null, () =>
      results.push("done")
    );

    expect(results).toEqual([1, 2, 3, "done"]);
  });
Handling of "firehose" synchronous data

With a cancellation token, like AbortSignal, handling synchronous firehoses and stopping them due to external unsubscription becomes a bit more intuitive than it was with previous designs, IMO:

 test("should handle firehose", () => {
    let loops = 0;
    const source = new Observable((next, err, complete, signal) => {
      for (let i = 0; i < 1000000000 && !signal.aborted; i++) {
        next(i);
        loops++;
      }
      // this will noop due to protections after abort below
      // which is "unsubscription".
      complete();
    });

    const controller = new AbortController();
    const results = [];

    source.subscribe(
      value => {
        results.push(value);
        if (results.length === 3) {
          // "unsubscribe"
          controller.abort();
        }
      },
      null,
      // complete should not be called, because of the
      // abort (unsubscription) above
      () => results.push("done"),
      controller.signal
    );

    expect(loops).toBe(3);
    expect(results).toEqual([0, 1, 2]);
  });
Concessions

first and last may not be necessary, and are more "nice to have"s for this type. Their primary use cases would be for wrapped HTTP calls, which, in a world where AbortSignals were prolific, should probably just be done via fetch.

Cons

There are a few cons to this design. Notably, from my perspective, it's not completely compatible with current popular designs. But I'm less worried about that than getting the appropriate primitives into the language.

Other thoughts

It's possible to have this implement Symbol.asyncIterator with a known behavior, like buffering all values internally until they are read. This, of course, comes with some potential issues around back-pressure and memory pressure, but I think that's easy to understand for most people who might use this type with for await.

Another con is creating a "Subject", which is a common type created to compose with observables, becomes mildly challenging, in that it would need to be something that could be destructured into three functions and an abort signal, but again, I don't think that's really a concern for language authors. The community can take care of that.

Links

I've tossed together a demo here.

Repo: https://github.com/benlesh/tc39-observable-proposal
Codesandbox: https://codesandbox.io/s/tc39-observable-proposal-proposed-change-uxh4p

slikts, rgbkrk, SerkanSipahi, fxck, johnlindquist and 47 moreshobhitg, SerkanSipahi, AdrienRedon, trotyl, alex-okrushko and 5 morefejes713, topaxi, seokju-na, SerkanSipahi, Itrulia and 18 morebrandonroberts, EstenGrove, SerkanSipahi, irustm, AdrienRedon and 7 morebrandonroberts, jcampuza, anbalase, SerkanSipahi, lxsmnsyc and 2 more


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4