Skip to content

Determine which Observable operators and extensions to include in angular core #4390

Closed

Description

@robwormald

Now that #2974 has landed, we're exposing the "bare" RxNext Observable without any operators included. This is partially because including the main Rx import caused everything to explode (as it has a large amount of files) and partially to avoid bloating the core codebase with another ~125k of operators.

The list of currently implemented operators is here: https://github.com/ReactiveX/RxJS/tree/master/src/operators

A core set of of them should be included, things like map, flatMap, flatMapLatest, take, filter, reduce, scan, merge, and toPromise. Additionally specific operators can be exposed for specific APIs - for example, Http should add retry and retryWhen (or some equivalent)

Also, a set of specific Observable utility implementations are available - things like Observable.from, Observable.fromArray, Observable.interval, etc. The list of these is here: https://github.com/ReactiveX/RxJS/tree/master/src/observables

Alternately we could fix the build issue and simply bring in the entire Rx module for now, and pare it down as we move closer to production and begin to optimize.

cc @jeffbcross @vsavkin

edit: the ES6 version is ~250k for the complete unminiified Rx, roughly ~half of which is operators. For CJS, its ~350k with again roughly half being operators.

Activity

  1. PatrickJS commented on Sep 28, 2015

    @PatrickJS
    Contributor

    can you also include Observable.of and Observable.defer?

  2. jeffbcross commented on Sep 28, 2015

    @jeffbcross
    Contributor

    It would be nice to provide a minimum set of operators, as you've described, by default from Angular APIs that export Observables. And to allow users who'd like a different set of operators, they should be able to override an ObservableFactory binding with DI (name not important right now). So when overriding that binding, calling http.get() would return an observable with the operators I need.

  3. jeffbcross commented on Sep 28, 2015

    @jeffbcross
    Contributor

    If we include an Observable factory in DI, it's worth considering whether the same should be done for Promise.

  4. gkalpak commented on Sep 28, 2015

    @gkalpak
    Member

    👍 and 👍 (1 for Observable and 1 for Promise factory).

  5. mhevery commented on Sep 29, 2015

    @mhevery
    Contributor

    I would vote to not include any in Angular2 and have people import what they need.

  6. robwormald commented on Sep 29, 2015

    @robwormald
    ContributorAuthor

    Problem with that is at the moment there's not really a good way to add them after the fact to Observables returned from the core, at least in TS:

    //without operators:
    http.get('foos.json').subscribe(res => {
      this.foos = res.json();
    });
    
    //with .map included, connected to an async pipe
    this.foos = http.get('foos.json').map(res => res.json());
    
    //with .retry() included
    this.foos = http.get('foos.json').retry(3).map(res => res.json());

    The ES7 functionBind proposal gives us a nice solution, but its not currently supported in TS:

    import {map, retry} from './myoperators'
    
    this.foos = http.get('foos.json')::retry(3)::map(res => res.json());

    Dart has a distinct advantage here as they provide a similar set in the language itself on Streams:
    https://api.dartlang.org/1.12.1/dart-async/Stream-class.html#instance-methods

  7. benlesh commented on Sep 30, 2015

    @benlesh
    Contributor

    Without es7 function bind support in TypeScript, I'd recommend just including the current RxJS 5 Core Operators. There are a lot, but frankly they're a subset, and they're things that people want.

    If you really don't want to include those, you could just mirror the Rx.ts file and create your own "lite" version... But then you'll be stuck supporting requests for the other operators.

    ... So my recommendation is use Rx as-is. And come make arguments to have ones removed you don't think are "core" enough.

  8. benlesh commented on Sep 30, 2015

    @benlesh
    Contributor

    FWIW, the ones in RxJS 5 Core are ones that are non-trivial to reproduce with any combination of other operators. That's the goal at least.

    Most of them were chosen because of their usage within Netflix apps.

  9. jeffbcross commented on Oct 5, 2015

    @jeffbcross
    Contributor

    We had a meeting with @IgorMinar, @Blesh and @robwormald today to discuss this issue. We wanted to come to a solution that would allow us to keep the byte size of angular2 core small, and make it easy for Rx power-users to easily get useful operators.

    My proposal was to use a "lite" Observable with only a subscribe method, and use DI and provide an ObservableFactory token that could be overwritten:

    import {Observable} from '@reactivex/rxjs/Rx';
    // Bind the included factory to a full-featured Observable with default set of operators
    bootstrap(MyComponent, [bind(ObservableFactory).toValue(Observable)]);

    @mhevery brings up the point that by requiring an injector, users could not take advantage of field assignment shorthand provided by TypeScript.

    class MyComponent {
      someEvent = new EventEmitter(); // EventEmitter couldn't use DI in this case, unless refactored to accept an Observable argument.
    }

    My counter-argument to that is that if users wanted to instantiate their own Subjects/Observables, they could just import the one the same one they've already bound to ObservableFactory token, and just instantiate it themselves without DI. It's up to whomever is consuming the component's events to know the shape of the Observable being provided. The benefit of the DI binding should only be for the angular framework itself to produce the shape of Observable that the consumer would like.

    However, there are still issues with type information with the DI approach. For angular APIs that say they return an Observable from Angular (Observable lite), any code that consumes such an Observable would not automatically know that the binding had been replaced with a new shape of Observable.

    Another discussion topic that came up was strategies for composing operators. Right now it's possible to compose operators onto an Observable instance using the lift method. The current RxJS design is prototype-based, so monkey-patching or extending are the way to add default operators.

    Random notes, I know, but wanted to get them out there before I forget.

    Did I miss or misrepresent anything @Blesh @robwormald @IgorMinar?

  10. benlesh commented on Oct 5, 2015

    @benlesh
    Contributor

    So to be clear, the lift method allows you to use an Operator from RxJS, which is a Subscriber factory to chain operators, but it's very unergonomic to do so.

    One could either:

    1. Decorate the prototype
    2. Subclass Observable, overriding lift to return the Subclass, and add whatever operators you care about to that.
    3. Use ES7 function bind, which is available in Babel, but not TypeScript.

    I think if you had an ObservableFactory create your Observable class for you, you could have that be responsible for decorating the Observable.prototype. I don't know enough about Angular's current DI, but I presume it can dynamically create classes to be injected, and not just object instances?

  11. 19 remaining items

  12. modified the milestones: alpha-41, on Oct 9, 2015
  13. jeffbcross commented on Oct 13, 2015

    @jeffbcross
    Contributor

    Moving this into 41, working on a simple solution to manually add the combinators we need to EventEmitter without changing anything about its inheritance.

  14. modified the milestones: alpha-41, on Oct 13, 2015
  15. modified the milestones: alpha-41, on Oct 13, 2015
  16. angular-automatic-lock-bot commented on Sep 7, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  17. locked and limited conversation to collaborators on Sep 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions