As this is a major version release, we have a few breaking changes we need to make sure every knows about, and we need to let all of you know how to migrate your apps to v6 as smoothly as possible.
To get started using v6 with your existing v5 code, please try the following steps:
- Update to the latest
5.5.X
version of RxJS. This will uncover any errors that might be a result of bugs in your code exposed by bug fixes. In particular a bug was fixed in5.5.6
that stopped some errors thrown synchronously in operators likemergeMap
from being properly propagated. (They were swallowed). So get pasted5.5.6
right away to make sure those issues are covered. - Install rxjs 6 via npm or yarn (e.g.
npm i -S rxjs@rc
) (TODO: Update this after rc is over) - Install
rxjs-compat
via npm or yarn (e.g.npm i -S rxjs-compat@rc
) (TODO: Update this after rc is over) - This library will enable imports from locations that are removed in v6, as well as provide the ability to use therxjs/add/operator/
-style imports. At this point, the app should be working for most of you. - TypeScript Users: Try installing and running
rxjs-lint
withtslint --fix
. This will automagically going through and update your code to be v6 compliant. You may need to run it more than once. More information can be found here: https://github.com/reactivex/rxjs-ts-lint - LAST DITCH: If steps 2-3 DO NOT work... you can try
rxjs@forward-compat
, (a.k.a5.6.0-forward-compat
). This package is almost exactly the same as v5.5, only it exports fromrxjs
just like v6 does, so importing the kitchen sink like some people did in v5 is the only thing that will break. - If you are STILL having problems. Please file an issue with any error messages or reproduction you can provide.
Major Changes are as follows:
There are now fewer entry points to the library. Instead of importing your RxJS types from all over
the library, you'll import from (generally) two locations: rxjs
and rxjs/operators
, there are a few more, but those are the main two.
Main export points:
rxjs
: All classes (Observable
,Subject
, etc), creation methods (from
,interval
,concat
,merge
etc), schedulers, utilities and helpers can be found here.rxjs/operators
: All operators can be found here (map
,filter
,mergeMap
, etc).
Other export points:
rxjs/testing
: TheTestScheduler
and friends can be found hererxjs/ajax
: This is the new home for the rxjs AJAX implementationrxjs/webSocket
: This is the home for the rxjs web socket implementation.
v6 | v5.5 | |
---|---|---|
any operator, such as mergeMap
|
import { mergeMap } from 'rxjs/operators'; |
import { mergeMap } from 'rxjs/operators/mergeMap'; import 'rxjs/add/operator/mergeMap'; import { mergeMap } from 'rxjs/operator/mergeMap'; |
creation methods like fromEvent or interval |
import { fromEvent } from 'rxjs'; |
import { fromEvent } from 'rxjs/observable/fromEvent'; import 'rxjs/add/observable/fromEvent'; |
schedulers like async are now asyncScheduler |
import { asyncScheduler } from 'rxjs'; |
import { async } from 'rxjs/scheduler/async'; |
utilities like pipe and noop |
import { pipe } from 'rxjs'; |
import { pipe } from 'rxjs/util/pipe'; |
There were many types exposed before that were really internal implementation details that people were using freely. Most notable
of these were the exposed Observable
classes, such as ErrorObservable
or ArrayObservable
that many people were using. Unfortunately,
we never intended for these classes to be used directly. Instead, their creation function counterparts should be used, as that was the
original intention.
v6 | v5 |
---|---|
from |
ArrayLikeObservable |
of |
ArrayObservable |
bindCallback |
BoundCallbackObservable |
bindNodeCallback |
BoundNodeCallbackObservable |
defer |
DeferObservable |
empty or EMPTY (constant) |
EmptyObservable |
throwError |
ErrorObservable |
forkJoin |
ForkJoinObservable |
fromEvent |
FromEventObservable |
fromEventPattern |
FromEventPatternObservable |
from |
FromObservable |
generate |
GenerateObservable |
iif |
IfObservable |
interval |
IntervalObservable |
from |
IteratorObservable |
NEVER (constant) |
NeverObservable |
pairs |
PairsObservable |
from |
PromiseObservable |
range |
RangeObservable |
of |
ScalarObservable |
timer |
TimerObservable |
using |
UsingObservable |
Just use from
. The reason is that any use of an operator/method that accepts an observable or something that can be observed (like a Promise
or Array
, etc), already
imports most of the from
implementation anyhow. fromPromise
just increased the API surface area.
All uncaught errors are no being thrown a new callstack via "host report errors". This basically means unhandled errors are thrown in a setTimeout
. The main
reason for doing this is to prevent a really nasty set of bugs calls "producer interfence", in which unhandled, synchronous errors thrown after a multicast would
break the mulicast for all listeners.
This means that some code that relied on synchronous error handling will now be broken. This includes, things like:
try {
source$.subscribe(() => {
throw new Error('bad');
});
} catch (err) {
// this will no longer ne hit.
}
or
expect(source$.pipe(x => { throw new Error('bad'); }))
.toThrow(new Error(bad));
// Will throw, but it will no longer pass.
errorIfEmpty<T>(errorFactory: () => any): Observable<T>
- If the source observable completes without emitting a value, the errorFactory
will be called and the returned error will be emitted as an error from the resulting Observable
. This operator was developed to mirror defaultIfEmpty
, and to enable creating smaller first
and last
operators.
Result selectors on operators like mergeMap
, switchMap
, etc, are deprecated and will be removed in version 7.
with resultSelector (v 5.x). NOTE: The concurrency limit argument is still optional, it is only shown here to be thorough.
source.pipe(
mergeMap(fn1, fn2, concurrency)
)
the same functionality without resultSelector, achieved with inner map.
source.pipe(
mergeMap((a, i) => fn1(a, i).pipe(
map((b, ii) => fn2(a, b, i, ii))
)),
concurrency
)
with resultSelector (v 5.x)
source.pipe(
mergeMapTo(a$, resultSelector)
)
without resultSelector
source.pipe(
mergeMap((x, i) => a$.pipe(
map((y, ii) => resultSelector(x, y, i, ii))
)
)
####concatMap
with resultSelector (v 5.x)
source.pipe(
concatMap(fn1, fn2)
)
the same functionality without resultSelector, achieved with inner map
source.pipe(
concatMap((a, i) => fn1(a, i).pipe(
map((b, ii) => fn2(a, b, i, ii))
)
)
with resultSelector (v 5.x)
source.pipe(
concatMapTo(a$, resultSelector)
)
without resultSelector
source.pipe(
concatMap((x, i) => a$.pipe(
map((y, ii) => resultSelector(x, y, i, ii))
)
)
with resultSelector (v 5.x)
source.pipe(
switchMap(fn1, fn2)
)
the same functionality without resultSelector, achieved with inner map
source.pipe(
switchMap((a, i) => fn1(a, i).pipe(
map((b, ii) => fn2(a, b, i, ii))
)
)
with resultSelector (v 5.x)
source.pipe(
switchMapTo(a$, resultSelector)
)
without resultSelector
source.pipe(
switchMap((x, i) => a$.pipe(
map((y, ii) => resultSelector(x, y, i, ii))
)
)
with resultSelector (v 5.x)
source.pipe(
exhaustMap(fn1, fn2)
)
the same functionality without resultSelector, achieved with inner map
source.pipe(
exhaustMap((a, i) => fn1(a, i).pipe(
map((b, ii) => fn2(a, b, i, ii))
)
)
with resultSelector (v5)
source.pipe(
first(predicate, resultSelector, defaultValue)
)
without resultSelector (if you're not using the index in it):
source.pipe(
first(predicate, defaultValue),
map(resultSelector)
)
without resultSelector (if you ARE using the index in it)
source.pipe(
map((v, i) => [v, i]),
first(([v, i]) => predicate(v, i)),
map(([v, i]) => resultSelector(v, i)),
)
with resultSelector (v5)
source.pipe(
last(predicate, resultSelector, defaultValue)
)
without resultSelector (if you're not using the index in it):
source.pipe(
last(predicate, defaultValue),
map(resultSelector)
)
without resultSelector (if you ARE using the index in it)
source.pipe(
map((v, i) => [v, i]),
last(([v, i]) => predicate(v, i)),
map(([v, i]) => resultSelector(v, i)),
)
with resultSelector
forkJoin(a$, b$, c$, resultSelector)
// or
forkJoin([a$, b$, c$], resultSelector)
without resultSelector
forkJoin(a$, b$, c$).pipe(
map(x => resultSelector(...x))
)
// or
forkJoin([a$, b$, c$]).pipe(
map(x => resultSelector(...x))
)
with resultSelector
zip(a$, b$, c$, resultSelector)
// or
zip([a$, b$, c$], resultSelector)
without resultSelector
zip(a$, b$, c$).pipe(
map(x => resultSelector(...x))
)
// or
zip([a$, b$, c$]).pipe(
map(x => resultSelector(...x))
)
with resultSelector
combineLatest(a$, b$, c$, resultSelector)
// or
combineLatest([a$, b$, c$], resultSelector)
without resultSelector
combineLatest(a$, b$, c$).pipe(
map(x => resultSelector(...x))
)
// or
combineLatest([a$, b$, c$]).pipe(
map(x => resultSelector(...x))
)
with resultSelector
fromEvent(button, 'click', resultSelector)
without resultSelector
fromEvent(button, 'click').pipe(
map(resultSelector)
)
never()
is deprecated and you should use NEVER
, which is a constant. empty()
without a scheduler is also deprecated in favor of EMPTY
which is a constant.