benlesh / symbol-observable

Symbol.observable ponyfill

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Broken interoperability between Rxjs and symbol-observable

ivstas opened this issue · comments

Hi.
Due to latest decision to avoid polyfilling Symbol.observable and 6.0 release going public, there is a difference between how symbol observable is being defined in Rxjs and this repo.

Basically, there are 3 cases:

  1. old browsers (no Symbol at all)
  2. modern browsers(Symbol is presented, but Symbol.observable is not)
  3. bright future (when we have Symbol.observable in global scope).

The problem is that for case #2 implementation differs:

Why is it an issue:

3rd party libraries relying on symbol-observable package to retrieve Observable will fail to get one from RxJs.

Possible solution

One solution I might think of is to avoid altering the global scope and simply fallback to @@observable (like RxJs does). However, it's a big breaking change for this package.

RxJS is now designed to have a polyfill loaded first, as polyfills should be.

@benlesh ok, and in this scenario (when libraries don't polyfill) this ponyfill should also avoid to alter the global scope.

Users of symbol-observable always get the same symbol, even if there will be multiple instances of the library, thus they depend on symbol-observable only.

Your implementation doesn't normalize Symbol.observable, thus you depend on globals, based on the hypothetical future standard and order of importing polyfills.
And of course, that implies importing rx to get your symbol

You should include all polyfills first, before any libraries. If you do that, RxJS will pick up the just-polyfilled Symbol.observable and use it.

And this package should be renamed to polyfill as opposed to ponyfill to reduce confusion :)

Just got hit by this. We're using redux (via read-dnd), Rxjs and mobx-utils.

Rxjs and mobx-utils use a ponyfill. Redux uses this polyfill.

The behaviour in our build was:

  1. Rxjs ponyfill imported, Symbol.observable not available so '@@observable' used.
  2. symbol-observable imported via redux, Symbol.observable set.
  3. mobx-utils imported, Symbol.observable is now available so used.

Rxjs and mobx-util observables are no longer compatible with each other.

This behaviour is expected for a polyfill, as other comments suggest polyfills must be imported before all other modules.

However this module very clearly advertises itself as a ponyfill. But this module is not pure like a pony.

So I suggest either:

  1. Be a good pony and don't have any side effects.
  2. Don't advertise as a ponyfill.

Rxjs ponyfill: https://github.com/ReactiveX/rxjs/blob/master/src/internal/symbol/observable.ts#L13
mobx utils ponyfill:https://github.com/mobxjs/mobx-utils/blob/master/src/observable-stream.ts#L6

How about ?

import { computed } from 'mobx'
import { Observable } from 'rxjs/Observable'
import { Observer } from 'rxjs/Observer'

export const toStream = <T>(expression: () => T): Observable<T> =>
  Observable.create((observer: Observer<T>) => {
    const computedValue = computed(expression)
    return computedValue.observe(change => observer.next(change.newValue))
  })

I got bitten by this issue too in a setup similar to @WearyMonkey's (redux + rxjs).

In my case I got the error only in tests using the TestScheduler (from 'rxjs/testing') and only when takeUntil was used.

Adding import 'symbol-observable'; to the top of my webpack entrypoint solved the issue.

You're advising random modules to use this module, meaning that random modules will polyfill the environment for me. No thanks. That's kinda mean.

import Symbol_observable from 'symbol-observable';
const Symbol_observable = Symbol && Symbol.observable || '@@observable';

No thanks

Sure, interop and agreements are developed only to let you break them. Obviously, there isn't enough inconsistency in the web, let's create brand new polyfill per project basis

let's create brand new polyfill per project basis

const Symbol_observable = Symbol && Symbol.observable || '@@observable'; is not a polyfill.

If that's all this module did, it would be great, and I would use it.

But, this module is a polyfill, it sets window.Symbol.observable. And it's not just the principle ... it actually breaks things because the polyfill occurs at whatever random point some random module that uses this module gets included.

In this issue, only rxjs breaks things, because bad interop is still better than a perfect disorder

This has been resolved for some time. I tried again to see if I could replicate it by importing from rxjs or symbol-observable in various orders. All is well:

https://codesandbox.io/s/vigilant-shirley-ve266?file=/src/index.ts

I believe this is still an issue:

Import rxjs into an environment where Symbol.observable isn't defined, then later import symbol-observable. rxjs will use the string "@@observable" while symbol-observable will use the symbol symbol(observable).

E.g. https://codesandbox.io/s/lucid-wind-csjbo?file=/src/index.ts . Passing an observable built with symbol-observable to rxjs causes rxjs to throw b/c it doesn't recognize the passed observable as an observable.

the issue only shows up when:

  • running in an environment where Symbol.observable isn't defined
  • rxjs is imported before symbol-observable (or any library depending on symbol-observable) is imported