@reactivex/rxjs
Version:
Reactive Extensions for modern JavaScript
302 lines (266 loc) • 12.1 kB
Markdown
# Migrating from RxJS 4 to 5
RxJS 5 is a ground-up rewrite of RxJS that actually began development when RxJS was in 2.0. This new version of RxJS had
three basic goals:
1. Better performance
2. Better debugging
3. Compliance with the [ES7 Observable Spec](https://github.com/zenparsing/es-observable)
Meeting the above goals meant breaking changes to the RxJS API, and a complete rewrite means that we had opportunity
to change/fix/correct things we've wanted to correct about RxJS in general.
## Key Component Classes Are Recomposed
They are similar to other language implementations of ReactiveX (e.g. RxJava).
|RxJS 4|RxJS 5| remarks |
|---|---|---|
|`Observer`|`Subscriber` implements `Observer`| `Observer` is an interface now |
|`IDisposable`|`Subscription`|`Subscription` is a class, not an interface.|
## Observer Interface Changes (also Subjects)
Due to wanting to comply with the ES7 Observable Spec (goal 3 above), the Observer interface (as implemented by
Observers and Subjects alike) has changed.
- `observer.onNext(value)` -> `observer.next(value)`
- `observer.onError(err)` -> `observer.error(err)`
- `observer.onCompleted()` -> `observer.complete()`
So what was once `subject.onNext("hi")` is now `subject.next("hi")`.
## Subscription `dispose` is now `unsubscribe`
To meet the Observable spec (goal 3) `dispose` had to be renamed to `unsubscribe`.
<!-- skip-example -->
```js
var subscription = myObservable.subscribe(doSomething);
// RxJS 4: subscription.dispose();
subscription.unsubscribe();
```
## Subscription: All Subscriptions Are "Composite"
In RxJS 4, there was the idea of a `CompositeSubscription`. Now all Subscriptions are "composite".
Subscription objects have an `add` and `remove` method on them useful for adding and removing subscriptions
enabling "composite" subscription behavior.
## Operators Renamed or Removed
|RxJS 4|RxJS 5|
|---|---|
|`amb`|`race`|
|`and`|No longer implemented|
|`asObservable`|Exists on `Subject` only|
|`average`|No longer implemented|
|`bufferWithCount`|`bufferCount`|
|`bufferWithTime`|`bufferTime`|
|`concat`|`concat`|
|`concatAll`|`concatAll`|
|`concatMap`|`concatMap`|
|`concatMapObserver`|No longer implemented|
|`controlled`|No longer implemented|
|`delaySubscription`|No longer implemented|
|`do`|`do`|
|`doAction`|`do`|
|`doOnCompleted`|`do(null, null, fn)`|
|`doOnError`|`do(null, fn)`|
|`doOnNext`|`do(fn)`|
|`doWhile`|No longer implemented|
|`extend`|No longer implemented|
|`flatMapFirst`|`exhaustMap`|
|`flatMapLatest`|`switchMap`|
|`flatMapWithMaxConcurrent`|`mergeMap` or `flatMap`(alias)|
|`flatMap`|`mergeMap` or `flatMap`(alias)|
|`fromCallback`|`bindCallback`|
|`fromNodeCallback`|`bindNodeCallback`|
|`groupByUntil`|`groupBy(keySelector, elementSelector, durationSelector)`|
|`groupJoin`|No longer implemented|
|`includes(v)`|`.first(x => x === v, () => true, false)`|
|`indexOf(v)`|`.map((x, i) => [x === v, i]).filter(([x]) => x).map(([_, i]) => i).first()`|
|`join`|No longer implemented|
|`jortSortUntil`|No longer implemented|
|`jortSort`|No longer implemented|
|`just(v)` or `just(a, b, c)`|`of(v)`, `of(a, b, c)`|
|`lastIndexOf`|`.map((x, i) => [x === v, i]).filter(([x]) => x).map(([_, i]) => i).last()`|
|`manySelect`|No longer implemented|
|`map(fn)`|`map(fn)`|
|`map(value)`|`mapTo(value)`|
|`maxBy(fn)`|`scan((s, v, i) => { let max = Math.max(s.max, fn(v, i)); return { max, value: max === s.max ? s.value : v }; }, { max: null, value: undefined }).last(x => x.max !== null, x => x.value)`|
|`minBy(fn)`|`scan((s, v, i) => { let min = Math.min(s.min, fn(v, i)); return { min, value: min === s.min ? s.value : v }; }, { min: null, value: undefined }).last(x => x.min !== null, x => x.value)`|
|`of`|`of`|
|`ofObjectChanges`|No longer implemented|
|`pausableBuffered`|No longer implemented|
|`pausable`|No longer implemented|
|`pluck`|`pluck`|
|`publishLast`|`publishLast`|
|`publishValue`|`publishBehavior`|
|`replay`|`publishReplay`|
|`return`|`of`|
|`selectConcatObserver`|No longer implemented|
|`selectConcat`|`concatMap`|
|`selectMany(fn)`|`mergeMap(fn)` or `flatMap(fn)` (alias)|
|`selectMany(observable)`|`mergeMapTo(observable)`|
|`selectManyObserver` or `flatMapObserver`|No longer implemented|
|`select`|`map`|
|`shareReplay`|`publishReplay().refCount()`|
|`shareValue`|No longer implemented|
|`singleInstance`|`share`|
|`skipLastWithTime`|No longer implemented|
|`skipLast`|No longer implemented|
|`skipUntilWithTime`|No longer implemented|
|`slice(start, end)`|`skip(start).take(end - start)`|
|`some`|`first(fn, () => true, false)`|
|`sum`|`reduce((s, v) => s + v, 0)`|
|`switchFirst`|`exhaust`|
|`takeLast`|`takeLast`|
|`takeLastBufferWithTime`|No longer implemented|
|`takeLastBuffer`|No longer implemented|
|`takeLastWithTime`|No longer implemented|
|`takeUntilWithTime`|No longer implemented|
|`tapOnCompleted(fn)`|`do(null, null, fn)`|
|`tapOnError(fn)`|`do(null, fn)`|
|`tapOnNext(fn)`|`do(fn)`|
|`tap`|`do`|
|`timestamp`|`map(v => ({ value: v, timestamp: Date.now() }))`|
|`toMap(keySelector)`|`reduce((map, v, i) => map.set(keySelector(v, i), v), new Map())`|
|`toMap(keySelector, elmentSelector)`|`reduce((map, v, i) => map.set(keySelector(v, i), elementSelector(v)), new Map())`|
|`toSet`|`reduce((set, v) => set.add(v))`|
|`transduce`|No longer implemented|
|`where`|`filter`|
|`windowWithCount`|`windowCount`|
|`windowWithTimeOrCount`|No longer implemented|
|`windowWithTime`|`windowTime`|
|`zip`|`zip`|
## Operator Splits
To reduce polymorphism and get better performance out of operators, some operators have been split into more than one operator:
<table>
<thead>
<tr>
<th></th>
<th>RxJS 4</th>
<th>RxJS 5</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2"><code>map</code></td>
<td><code>map(project: function, thisArg?: any)</code></td>
<td><code>map(project: function, thisArg?: any)</code></td>
</tr>
<tr>
<td><code>map(value: any)</code></td>
<td><code>mapTo(value: any)</code></td>
</tr>
<tr>
<td rowspan="2"><code>flatMap</code></td>
<td><code>flatMap(project: function, resultSelector?: function)</code></td>
<td><code>flatMap(project: function, resultSelector?: function)</code></td>
</tr>
<tr>
<td><code>flatMap(value: Observable, resultSelector?: function)</code></td>
<td><code>flatMapTo(value: Observable, resultSelector?: function)</code></td>
</tr>
<tr>
<td rowspan="2"><code>switchMap</code> (aka <code>flatMapLatest</code>)</td>
<td><code>flatMapLatest(project: function, resultSelector?: function)</code></td>
<td><code>switchMap(project: function, resultSelector?: function)</code></td>
</tr>
<tr>
<td><code>flatMapLatest(value: Observable, resultSelector?: function)</code></td>
<td><code>switchMapTo(value: Observable, resultSelector?: function)</code></td>
</tr>
<tr>
<td rowspan="2"><code>concatMap</code></td>
<td><code>concatMap(project: function, resultSelector?: function)</code></td>
<td><code>concatMap(project: function, resultSelector?: function)</code></td>
</tr>
<tr>
<td><code>concatMap(value: Observable, resultSelector?: function)</code></td>
<td><code>concatMapTo(value: Observable, resultSelector?: function)</code></td>
</tr>
<tr>
<td rowspan="3"><code>buffer</code></td>
<td><code>buffer(closings: Observable)</code></td>
<td><code>buffer(closings: Observable)</code></td>
</tr>
<tr>
<td><code>buffer(closingNotifierFactory: function)</code></td>
<td><code>bufferWhen(closingNotifierFactory: function)</code></td>
</tr>
<tr>
<td><code>buffer(openings: Observable, closingSelector?: function)</code></td>
<td><code>bufferToggle(openings: Observable, closingSelector?: function)</code></td>
</tr>
<tr>
<td rowspan="3"><code>window</code></td>
<td><code>window(closings: Observable)</code></td>
<td><code>window(closings: Observable)</code></td>
</tr>
<tr>
<td><code>window(closingNotifierFactory: function)</code></td>
<td><code>windowWhen(closingNotifierFactory: function)</code></td>
</tr>
<tr>
<td><code>window(openings: Observable, closingSelector?: function)</code></td>
<td><code>windowToggle(openings: Observable, closingSelector?: function)</code></td>
</tr>
<tr>
<td rowspan="2"><code>debounce</code></td>
<td><code>debounce(durationSelector: Observable)</code></td>
<td><code>debounce(durationSelector: Observable)</code></td>
</tr>
<tr>
<td><code>debounce(delay: number, scheduler?: Scheduler)</code></td>
<td><code>debounceTime(delay: number, scheduler?: Scheduler)</code></td>
</tr>
<tr>
<td><code>throttle</code></td>
<td><code>throttle(delay: number, scheduler?: Scheduler)</code></td>
<td><code>throttleTime(delay: number, scheduler?: Scheduler)</code></td>
</tr>
<tr>
<td rowspan="2"><code>delay</code></td>
<td><code>delay(dueTime: number|Date, scheduler?: Scheduler)</code></td>
<td><code>delay(dueTime: number|Date, scheduler?: Scheduler)</code></td>
</tr>
<tr>
<td><code>delay(subscriptionDelay?: Observable<any>, delayDurationSelector: function)</code></td>
<td><code>delayWhen(delayDurationSelector: function, subscriptionDelay?: Observable<any>)</code></td>
</tr>
<tr>
<td rowspan="2"><code>timeout</code></td>
<td><code>timeout(dueTime: number | Date, other?: Error, scheduler?: Scheduler)</code></td>
<td><code>timeout(due: number | Date, errorToSend?: any, scheduler?: Scheduler)</code></td>
</tr>
<tr>
<td><code>timeout(dueTime: number | Date, other?: Observable | Promise, scheduler?: Scheduler)</code></td>
<td><code>timeoutWith(due: number | Date, withObservable: ObservableInput, scheduler: Scheduler)</code></td>
</tr>
<tr>
<td rowspan="2"><code>sample</code></td>
<td><code>sample(interval: number, scheduler?: Scheduler)</code></td>
<td><code>sampleTime(interval: number, scheduler?: Scheduler)</code></td>
</tr>
<tr>
<td><code>sample(notifier: Observable)</code></td>
<td><code>sample(notifier: Observable)</code></td>
</tr>
</tbody>
</table>
## Operator Interface Changes
<table>
<thead>
<tr>
<th></th>
<th>RxJS 4</th>
<th>RxJS 5</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>distinctUntilChanged</code></td>
<td><code>distinctUntilChanged(keySelector: function, comparer: function)</code></td>
<td><code>distinctUntilChanged<T, K>(compare?: (x: K, y: K) => boolean, keySelector?: (x: T) => K): Observable<T></code></td>
</tr>
</tbody>
</table>
## Default Scheduling Changed
RxJS v4 defaulted to a scheduler called `Rx.Scheduler.asap` which schedules on the micro task queue. RxJS v5 however defaults to having no scheduler at all; v4 called this `Rx.Scheduler.immediate`. This was done to increase performance for the most common use cases.
## Schedulers Renamed
The names of the Schedulers in RxJS 4 were based off of the Rx.NET implementation. Consequently, some of the names
didn't make sense in a JavaScript context (for example: `currentThread` when there's only one thread anyhow).
|RxJS 4|RxJS 5||
|------|------|---|
|`Rx.Scheduler.default`|`Rx.Scheduler.asap`|schedules on the micro task queue|
|`Rx.Scheduler.currentThread`|`Rx.Scheduler.queue`|schedules on a queue in the current event frame (trampoline scheduler)|
|`Rx.Scheduler.immediate`|`undefined`|by not passing a scheduler to operators that request it, it defaults to recursive execution|
## Unimplemented Operators/Features
If there is a feature that used to exist in RxJS 4, but no longer does in RxJS 5, please be sure to file an issue (and preferably a PR).
In your issue, please describe the use case you have for the operator so we can better understand your need and prioritize it, and/or find
and alternative way to compose the desired behavior.