Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Symbol.observable #677

Closed
mweststrate opened this issue Nov 21, 2016 · 18 comments
Closed

Support Symbol.observable #677

mweststrate opened this issue Nov 21, 2016 · 18 comments

Comments

@mweststrate
Copy link
Member

Not sure about exact api yet. Probably similar to observe? Or a utility that takes an expression that is tracked, and it's output can be subcribed to?

@mweststrate
Copy link
Member Author

Possible streams to export:

  1. Stream per object / array, with change (e.g. observe(x) as stream
  2. Stream per object / array property (e.g. observe(x, prop) as stream (also for computeds)
  3. Stream per object / array, as shallow copy stream
  4. tracked-function to stream, so like reaction() but then creating a stream

Reading streams:

  1. stream to boxed observable

@benjamingr what is your use case?

@benjamingr
Copy link
Member

benjamingr commented Nov 22, 2016

I want to use mobx observables inside Rx operators like:

someRx.switchMap(() => this.someMobX)...

And have someMobx be assimilated as an observable cleanly. Currently I have code like:

var autocomplete = toRx(mobxForInput).
  filter(Boolean).
  switchMap(value => fetch("/api/"+value)).
  toMobX() 

For autocomplete for example.

In addition it would be great if the other way around worked too:

@observable foo = someRxStream; // when someRxStream emits a value, mobx change to it

@benjamingr
Copy link
Member

Ping @Blesh @spion

@benlesh
Copy link

benlesh commented Dec 12, 2016

The implementation is basically to add a [Symbol.observable] property to any object, which is a function that returns an Object with the interface:

interface IObserver {
  next(value): void;
  error(error): void;
  complete(value): void;
}

interface ISubscription {
  unsubscribe(): void;
}

interface IObservable {
  subscribe(observer: IObserver): ISubscription
  [Symbol.observable](): IObservable;
}

So a minimal version of this would be as simple as:

SomeObject.prototype[Symbol.observable] = function () {
  return {
    [Symbol.observable]() { return this; }
    subscribe(observer) {
      observer.next('hello world');
      observer.complete();
      return {
        unsubscribe() { console.log('teardown here'); }
      };
    }
};

I hope that helps.

@benlesh
Copy link

benlesh commented Dec 12, 2016

Also, worth noting... this is the widely used current API. As the proposal in the TC39 changes (slowly), this is liable to change. However all changes that I know of should be something that supports backwards compatibility.

@mweststrate
Copy link
Member Author

@Blesh how is Symbol.observable typically emulated in ES5 environments? Is there a convenient for it, like with iterable: "@@observable"?

@mweststrate
Copy link
Member Author

@Blesh Never mind, found it

mweststrate added a commit to mobxjs/mobx-utils that referenced this issue Dec 24, 2016
@mweststrate
Copy link
Member Author

@benjamingr Figured these conversions methods are a better fit for mobxUtils (like the promise conversion methods). Just released 1.1.3 which introduces toStream / fromStream. Let me know whether it works out!

@benjamingr
Copy link
Member

@mweststrate since Symbol.observable is a JS standard I think it would be better in core.

Optimally, I'd like:

@observable debouncedClickDelta = fromEvent(...).filter(...).map(...)

So that observable unwraps things with Symbol.observable automatically.

Additionally, if we support Symbol.observable we can do something like:

Rx.Observable
  .fromEvent(button, "click")
  .map(e => computed(user.firstname + user.lastName))
  .distinctUntilChanged()
  .scan(nameChanges => nameChanges + 1, 0)
  .subscribe(nameChanges => console.log("Changed name ", nameChanges, "times"))

Which is arguably a lot cleaner.

@mweststrate
Copy link
Member Author

@benjamingr I can live with the second example (note that it would be e => computed(() => user.firstname + user.lastName), makes sense.

But for the first example, how should one ever dispose the stream?

@benjamingr
Copy link
Member

@mweststrate the same way you dispose any other observable? One MobX observable subscribing to one Rx observable.

@benjamingr
Copy link
Member

Would a clarification help?

@mweststrate mweststrate mentioned this issue Jan 6, 2017
19 tasks
@mweststrate
Copy link
Member Author

@benjamingr for the disposing in the first example; yes; the subscription to the observable stream should be disposed at some time. But @observable don't have an 'end of life' like mechanism (like reactions or computed values), so there is no clear point where we could hook up the unsubscription.

There is the notion of onBecome(Un)observed though, we could hook that up, but that means the value of the observable is not reliable if there are no (mobx) observer

@benjamingr
Copy link
Member

I don't mind if instead of:

@observable debouncedClickDelta

It's:

@computed debouncedClickDelta

Or something like that.

@LeonardoGentile
Copy link

ping

@benjamingr
Copy link
Member

@benlesh what is Rx currently doing? Is Symbol.observable still future proof?

@benlesh
Copy link

benlesh commented Jun 6, 2018

@benjamingr we're still using Symbol.observable. It's used by a lot of libraries now, it would take a lot of work and coordination to swing it to something else.

@danielkcz
Copy link
Contributor

I suppose this is kinda outdated and nobody seems interested in implementing it. Let's close it and I will reference #2327 just in case it wants to happen there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants