RxJS
tips & tricks
Wojciech Trawiński
Gdańsk, 2019
About me
JavaScript developer working at 7N for Roche company.
Passionate of Angular, RxJS, TypeScript and functional programming.
About me
JavaScript developer working at 7N for Roche company.
Passionate of Angular, RxJS, TypeScript and functional programming.
Owner of JavaScript everyday blog
Writer for Angular In Depth and Angular Love blogs
Agenda
Powerful overloads
Errors handling
Multicasting
Flattening operators
Miscellaneous stuff
Powerful overloads
Powerful overloads
first operator
Powerful overloads
import { interval } from 'rxjs';
import { first } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
first()
);
result$.subscribe(console.log);
//console output: 0
first operator
without arguments
Powerful overloads
import { interval } from 'rxjs';
import { first } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
first()
);
result$.subscribe(console.log);
//console output: 0
first operator
without arguments
import { interval } from 'rxjs';
import { first } from 'rxjs/operators';
const source$ = interval(1000);
const greaterThan = (threshold: number) => (
(val: number) => val > threshold
);
const result$ = source$.pipe(
first(greaterThan(5))
);
result$.subscribe(console.log);
//console output: 6
with predicate fn
Powerful overloads
filter operator
Powerful overloads
filter operator
with predicate fn
import { of } from 'rxjs';
import { filter } from 'rxjs/operators';
const source$ = of('foo', 69);
const havingType = (type: string) => (
(val: any) => typeof val === type
);
const result$ = source$.pipe(
filter(havingType('string'))
);
result$.subscribe(console.log);
//console output: 'foo'
Powerful overloads
filter operator
import { of } from 'rxjs';
import { filter, map } from 'rxjs/operators';
const source$ = of('foo', 69);
const havingType = (type: string) => (
(val: any) => typeof val === type
);
const result$ = source$.pipe(
filter(havingType('string')),
map(({length})=> length)
);
result$.subscribe(console.log);
Fail!
with predicate fn
Powerful overloads
filter operator
with predicate fn
with type predicate
import { of } from 'rxjs';
import { filter, map } from 'rxjs/operators';
const source$ = of('foo', 69);
const result$ = source$.pipe(
filter((item): item is string => (
typeof item === 'string'
)),
map(({length})=> length)
);
result$.subscribe(console.log);
//console output: 3
import { of } from 'rxjs';
import { filter } from 'rxjs/operators';
const source$ = of('foo', 69);
const havingType = (type: string) => (
(val: any) => typeof val === type
);
const result$ = source$.pipe(
filter(havingType('string'))
);
result$.subscribe(console.log);
//console output: 'foo'
Powerful overloads
distinctUntilChanged operator
Powerful overloads
distinctUntilChanged operator
without arguments
import { of } from 'rxjs';
import { distinctUntilChanged } from ...;
const foo = {a: 1};
const bar = {a: 1};
const source$ = of(foo, bar);
const result$ = source$.pipe(
distinctUntilChanged()
);
result$.subscribe(console.log);
//console output: {a: 1}, {a: 1}
Powerful overloads
distinctUntilChanged operator
without arguments
import { of } from 'rxjs';
import { distinctUntilChanged } from ...;
const foo = {a: 1};
const bar = {a: 1};
const source$ = of(foo, bar);
const result$ = source$.pipe(
distinctUntilChanged()
);
result$.subscribe(console.log);
//console output: {a: 1}, {a: 1}
with compare fn
import { of } from 'rxjs';
import { distinctUntilChanged } from ...;
const foo = {a: 1};
const bar = {a: 1};
const source$ = of(foo, bar);
const result$ = source$.pipe(
distinctUntilChanged((prev, next) => (
prev.a === next.a
))
);
result$.subscribe(console.log);
//console output: {a: 1}
Errors handling
Errors handling
import { interval } from 'rxjs';
import { tap } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
})
);
Errors handling
do nothing
result$.subscribe(console.log);
//console output: 0, 'Error: Bazinga!'
import { interval } from 'rxjs';
import { tap } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
})
);
Errors handling
do nothing
result$.subscribe(console.log);
//console output: 0, 'Error: Bazinga!'
provide error notification cb
result$.subscribe({
next: console.log,
error: err => console.log('Handling error'),
complete: () => console.log('Stream completed!')
});
//console output: 0, 'Handling error'
import { interval } from 'rxjs';
import { tap } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
})
);
Errors handling
import { interval, of } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
}),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
catch error and provide fallback value
Errors handling
result$.subscribe({
next: console.log,
error: err => console.log('Handling error'),
complete: () => console.log('Stream completed!')
});
//console output: 0, 'Handling error locally', 'Fallback value', 'Stream completed!'
catch error and provide fallback value
import { interval, of } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
}),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
Errors handling
retry observable at most n times
import { interval, of } from 'rxjs';
import { tap, retry, catchError } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
}),
retry(2),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
Errors handling
import { interval, of } from 'rxjs';
import { tap, retry, catchError } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
}),
retry(2),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
result$.subscribe({
next: console.log,
error: err => console.log('Handling error'),
complete: () => console.log('Stream completed!')
});
//console output: 0, 0, 0 'Handling error locally', 'Fallback value', 'Stream completed!'
retry observable at most n times
Errors handling
import { interval, of } from 'rxjs';
import { tap, retryWhen, delay, catchError } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if (val === 1) {
throw new Error('Bazinga!');
}
}),
retryWhen(errors => errors.pipe(
delay(2000),
tap(() => console.warn('Retrying...')),
)),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
retry under certain circumstances
retry when the notifier observable emits
Errors handling
import { interval, of } from 'rxjs';
import { tap, retryWhen, delay, take, catchError } from 'rxjs/operators';
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if (val === 1) {
throw new Error('Bazinga!');
}
}),
retryWhen(errors => errors.pipe(
delay(2000),
tap(() => console.warn('Retrying...')),
take(2)
)),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
retry under certain circumstances
result$ completes without receiving the error
Errors handling
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if (val === 1) {
throw new Error('Bazinga!');
}
}),
retryWhen(errors => errors.pipe(
delay(2000),
tap(error => {
if (error.message === 'Bazinga!') {
throw error;
}
console.warn('Retrying...')
}),
take(2)
)),
catchError(err => {
console.log('Handling error locally');
return of('Fallback value')
})
);
retry under certain circumstances
catchError handles
the rethrown error
Multicasting
...making cold observable hot...
Hot & Cold Observables
Hot & Cold Observables
Hot
Observable closes over notifications producer
…like watching a movie at the cinema:
- single source for all observers,
- if you’re late, you’ll miss the beginning.
Hot & Cold Observables
Hot
Observable closes over notifications producer
…like watching a movie at the cinema:
- single source for all observers,
- if you’re late, you’ll miss the beginning.
Cold
Notifications producer created at subscription
…like watching a movie on Netflix:
- each observer gets own source,
- no way to be late.
Multicasting
import { ajax } from 'rxjs/ajax';
import { tap, map } from 'rxjs/operators';
const source$ = ajax('https://jsonplaceholder.typicode.com/users/1').pipe(
tap(() => console.log('Making http request...')),
map(({ response }) => response)
);
const address$ = source$.pipe(
map(({ address }) => address)
);
const company$ = source$.pipe(
map(({ company }) => company)
);
Multicasting
import { ajax } from 'rxjs/ajax';
import { tap, map } from 'rxjs/operators';
const source$ = ajax('https://jsonplaceholder.typicode.com/users/1').pipe(
tap(() => console.log('Making http request...')),
map(({ response }) => response)
);
const address$ = source$.pipe(
map(({ address }) => address)
);
const company$ = source$.pipe(
map(({ company }) => company)
);
address$.subscribe(console.log)
company$.subscribe(console.log)
Two http requests!
Multicasting
import { ajax } from 'rxjs/ajax';
import { tap, map, share } from 'rxjs/operators';
const source$ = ajax('https://jsonplaceholder.typicode.com/users/1').pipe(
tap(() => console.log('Making http request...')),
map(({ response }) => response),
share()
);
const address$ = source$.pipe(
map(({ address }) => address)
);
const company$ = source$.pipe(
map(({ company }) => company)
);
address$.subscribe(console.log)
company$.subscribe(console.log)
Single http request!
share operator
Multicasting
address$.subscribe(console.log)
setTimeout(() => {
company$.subscribe(console.log)
}, 5000)
Two http requests!
import { ajax } from 'rxjs/ajax';
import { tap, map, share } from 'rxjs/operators';
const source$ = ajax('https://jsonplaceholder.typicode.com/users/1').pipe(
tap(() => console.log('Making http request...')),
map(({ response }) => response),
share()
);
const address$ = source$.pipe(
map(({ address }) => address)
);
const company$ = source$.pipe(
map(({ company }) => company)
);
Multicasting
import { ajax } from 'rxjs/ajax';
import { tap, map, shareReplay } from 'rxjs/operators';
const source$ = ajax('https://jsonplaceholder.typicode.com/users/1').pipe(
tap(() => console.log('Making http request...')),
map(({ response }) => response),
shareReplay(1)
);
const address$ = source$.pipe(
map(({ address }) => address)
);
const company$ = source$.pipe(
map(({ company }) => company)
);
address$.subscribe(console.log)
setTimeout(() => {
company$.subscribe(console.log)
}, 5000)
Single http request!
shareReplay operator
Flattening operators
switchMap
mergeMap
concatMap
exhaustMap
Flattening operators
observable input
- subscribable,
- promise,
- array-like,
- iterable.
Flattening operators
observable input
return observable
import { of, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
const actions$ = of('Login Success Action');
const newActions = ['Hide Spinner Action','Save Token Action'];
const effect$ = actions$.pipe(
switchMap(() => from(newActions))
);
effect$.subscribe(console.log);
//console output:
'Hide Spinner Action',
'Save Token Action'
Flattening operators
observable input
return observable
import { of, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
const actions$ = of('Login Success Action');
const newActions = ['Hide Spinner Action','Save Token Action'];
const effect$ = actions$.pipe(
switchMap(() => from(newActions))
);
effect$.subscribe(console.log);
//console output:
'Hide Spinner Action',
'Save Token Action'
return array
import { of, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
const actions$ = of('Login Success Action');
const newActions = ['Hide Spinner Action','Save Token Action'];
const effect$ = actions$.pipe(
switchMap(() => newActions)
);
effect$.subscribe(console.log);
//console output:
'Hide Spinner Action',
'Save Token Action'
Flattening operators
errors handling
Flattening operators
errors handling
import { of, interval } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { switchMap, map, take, catchError } from 'rxjs/operators';
const source$ = interval(2000).pipe(
map(val => val === 1 ? 1000000 : val + 1),
take(5)
);
const result$ = source$.pipe(
switchMap(val => ajax(`https://jsonplaceholder.typicode.com/users/${val}`).pipe(
map(({ response }) => response)
)),
catchError(err => of('Fallback value'))
);
result$.subscribe({
next: console.log,
error: console.error,
complete: () => console.log('Completed!')
});
Flattening operators
errors handling
import { of, interval } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { switchMap, map, take, catchError } from 'rxjs/operators';
const source$ = interval(2000).pipe(
map(val => val === 1 ? 1000000 : val + 1),
take(5)
);
const result$ = source$.pipe(
switchMap(val => ajax(`https://jsonplaceholder.typicode.com/users/${val}`).pipe(
map(({ response }) => response)
)),
catchError(err => of('Fallback value'))
);
result$.subscribe({
next: console.log,
error: console.error,
complete: () => console.log('Completed!')
});
Flattening operators
errors handling
import { of, interval } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { switchMap, map, take, catchError } from 'rxjs/operators';
const source$ = interval(2000).pipe(
map(val => val === 1 ? 1000000 : val + 1),
take(5)
);
const result$ = source$.pipe(
switchMap(val => ajax(`https://jsonplaceholder.typicode.com/users/${val}`).pipe(
map(({ response }) => response),
catchError(err => of('Fallback value'))
))
);
result$.subscribe({
next: console.log,
error: console.error,
complete: () => console.log('Completed!')
});
takeUntil operator
...when order matters...
takeUntil operator
Emits the values emitted by the source Observable
until a notifier Observable emits a value
takeUntil operator
Emits the values emitted by the source Observable
until a notifier Observable emits a value
import { interval, timer } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
const source$ = interval(1000);
const notifier$ = timer(2500);
const result$ = source$.pipe(
takeUntil(notifier$)
);
result$.subscribe({
next: console.log,
complete: () => console.log('Completed!')
});
//console output: 0, 1, 'Completed!'
takeUntil operator
with flattening operators
import { interval, timer } from 'rxjs';
import { takeUntil, map, switchMapTo} from 'rxjs/operators';
const source$ = interval(1000);
const notifier$ = timer(2500);
const innerSource$ = interval(300).pipe(
map(idx => `Inner ${idx}`)
);
const result$ = source$.pipe(
takeUntil(notifier$),
switchMapTo(innerSource$)
);
result$.subscribe({
next: console.log,
complete: () => console.log('Completed!')
});
//console output: 'Inner 0', 'Inner 1', 'Inner 2', 'Inner 0' ...
takeUntil operator
import { interval, timer } from 'rxjs';
import { takeUntil, map, switchMapTo} from 'rxjs/operators';
const source$ = interval(1000);
const notifier$ = timer(2500);
const innerSource$ = interval(300).pipe(
map(idx => `Inner ${idx}`)
);
const result$ = source$.pipe(
takeUntil(notifier$),
switchMapTo(innerSource$)
);
result$.subscribe({
next: console.log,
complete: () => console.log('Completed!')
});
//console output: 'Inner 0', 'Inner 1', 'Inner 2', 'Inner 0' ...
with flattening operators
Memory leak!
Don't do that at home!
takeUntil operator
import { interval, timer } from 'rxjs';
import { takeUntil, map, switchMapTo} from 'rxjs/operators';
const source$ = interval(1000);
const notifier$ = timer(2500);
const innerSource$ = interval(300).pipe(
map(idx => `Inner ${idx}`)
);
const result$ = source$.pipe(
switchMapTo(innerSource$),
takeUntil(notifier$)
);
result$.subscribe({
next: console.log,
complete: () => console.log('Completed!')
});
//console output: 'Inner 0', 'Inner 1', 'Inner 2', 'Inner 0', 'Completed!'
with flattening operators
Order matters!
defer function
...just because you have not subscribed doesn't mean that nothing happens...
defer function
Promise recap
const fetchMessage = () => Promise.resolve('Angular rocks!').then(message => {
console.log('Some internal stuff when promise is resolved');
return message;
});
fetchMessage().then(message => console.log( `Received ${message}`));
//console output: 'Some internal stuff...', 'Received Angular rocks!'
defer function
Promise recap
const fetchMessage = () => Promise.resolve('Angular rocks!').then(message => {
console.log('Some internal stuff when promise is resolved');
return message;
});
fetchMessage().then(message => console.log( `Received ${message}`));
//console output: 'Some internal stuff...', 'Received Angular rocks!'
const fetchMessage = () => Promise.resolve('Angular rocks!').then(message => {
console.log('Some internal stuff when promise is resolved');
return message;
});
fetchMessage();
//console output: 'Some internal stuff...'
Promise is eager!
defer function
Observable from Promise
import { from } from 'rxjs';
const fetchMessage = () => Promise.resolve('Angular rocks!').then(message => {
console.log('Some internal stuff when promise is resolved');
return message;
});
const source$ = from(fetchMessage());
window.requestIdleCallback(() => {
console.log('Have not subscribed yet!')
source$.subscribe(console.log)
});
//console output: 'Some internal stuff...', 'Have not subscribed yet!', 'Angular rocks!'
Promise resolved at the moment of creation
- before calling subscribe on Observable
Wait until micro tasks queue is empty
defer function
Creates an Observable that, on subscribe, calls an Observable factory to make an Observable for each new Observer
defer function
import { defer, from } from 'rxjs';
const fetchMessage = () => Promise.resolve('Angular rocks!').then(message => {
console.log('Some internal stuff when promise is resolved');
return message;
});
const source$ = defer(() => from(fetchMessage()));
window.requestIdleCallback(() => {
console.log('Have not subscribed yet!')
source$.subscribe(console.log)
});
//console output: 'Have not subscribed yet!', 'Some internal stuff...', 'Angular rocks!'
Promise resolved at the moment of creation,
but it's created when calling subscribe on Observable
iif function
Decides at subscription time
which Observable will actually be subscribed
iif function
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
const messagesCache = {
'angular': 'Angular rocks!',
'react': 'No comments'
};
function getMessageFromBE(query: string): Observable<string> {
return of(`Message from BE for ${query} query`).pipe(
delay(2000)
);
}
Call BE or return cached value if present
iif function
import { Observable, of, asyncScheduler, iif } from 'rxjs';
import { delay } from 'rxjs/operators';
const messagesCache = {
'angular': 'Angular rocks!',
'react': 'No comments'
};
function getMessageFromBE(query: string): Observable<string> {
return of(`Message from BE for ${query} query`).pipe(
delay(2000)
);
}
function fetchMessage(query: string): Observable<string> {
const cachedMessage = messagesCache[query];
return iif(
() => cachedMessage !== undefined,
of(cachedMessage, asyncScheduler),
getMessageFromBE(query)
);
}
fetchMessage('vue').subscribe(console.log);
fetchMessage('angular').subscribe(console.log);
//console output: 'Angular rocks!', 'Message from BE for vue query'
iif function
Complete immediately
import { iif, interval } from 'rxjs';
import { map } from 'rxjs/operators';
let hasAccess = true;
const messages$ = interval(1000).pipe(
map(idx => `Message ${idx}`)
);
function getMessagesStream() {
return iif(() => hasAccess, messages$);
}
Default value for streams provided to iif function is EMPTY
iif function
import { iif, interval } from 'rxjs';
import { map } from 'rxjs/operators';
let hasAccess = true;
const messages$ = interval(1000).pipe(
map(idx => `Message ${idx}`)
);
function getMessagesStream() {
return iif(() => hasAccess, messages$);
}
getMessagesStream().subscribe({
next: m => console.log(`User#1: ${m}`)
})
hasAccess = false;
getMessagesStream().subscribe({
next: m => console.log(`User#2: ${m}`),
complete: () => console.log('User#2: Completed')
});
console output: 'User#2: Completed', 'User#1: Message 0', ...
Mocking with of
Mocking with of
const todosServiceStub = {
get() {
return of([{id: 1}]);
}
};
describe('TodosComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ TodosComponent ],
providers: [{provide: TodosService, useValue: todosServiceStub}]
})
});
...
});
That’s what I call cheating
Mocking with of
Synchronous version
import { of } from 'rxjs';
const mockResponse = ['foo', 'bar', 'baz'];
const response$ = of(mockResponse);
response$.subscribe(console.log);
console.log('I am sync');
//console output: ['foo', 'bar', 'baz'],
'I am sync'
Mocking with of
Synchronous version
import { of } from 'rxjs';
const mockResponse = ['foo', 'bar', 'baz'];
const response$ = of(mockResponse);
response$.subscribe(console.log);
console.log('I am sync');
//console output: ['foo', 'bar', 'baz'],
'I am sync'
Asynchronous version
import { of, asyncScheduler } from 'rxjs';
const mockResponse = ['foo', 'bar', 'baz'];
const response$ = of(mockResponse, asyncScheduler);
response$.subscribe(console.log);
console.log('I am sync');
//console output: 'I am sync',
['foo', 'bar', 'baz']
No more cheating with async scheduler!
Handling subscriptions
Handling subscriptions
import { Subscription } from 'rxjs';
class SubsManager {
private subs: Subscription[] = [];
add(sub: Subscription) {
this.subs.push(sub);
}
cleanup() {
this.subs.forEach(sub => sub.unsubscribe());
}
}
Handling subscriptions
import { Subscription } from 'rxjs';
class SubsManager {
private subs: Subscription[] = [];
add(sub: Subscription) {
this.subs.push(sub);
}
cleanup() {
this.subs.forEach(sub => sub.unsubscribe());
}
}
Object Oriented Programming
Single Responsibility Principle
Handling subscriptions
import { interval, Subscription } from 'rxjs';
class SubsManager {
private subs: Subscription[] = [];
add(sub: Subscription) {
this.subs.push(sub);
}
cleanup() {
this.subs.forEach(sub => sub.unsubscribe());
}
}
const subsManager = new SubsManager();
const source$ = interval(1000);
subsManager.add(source$.subscribe());
subsManager.add(source$.subscribe());
subsManager.add(source$.subscribe());
setTimeout(() => {
subsManager.cleanup();
}, 5000);
Handling subscriptions
You can do better!
import { interval, Subscription } from 'rxjs';
class SubsManager {
private subs: Subscription[] = [];
add(sub: Subscription) {
this.subs.push(sub);
}
cleanup() {
this.subs.forEach(sub => sub.unsubscribe());
}
}
const subsManager = new SubsManager();
const source$ = interval(1000);
subsManager.add(source$.subscribe());
subsManager.add(source$.subscribe());
subsManager.add(source$.subscribe());
setTimeout(() => {
subsManager.cleanup();
}, 5000);
Handling subscriptions
import { interval, Subscription } from 'rxjs';
const subs = new Subscription();
const source$ = interval(1000);
subs.add(source$.subscribe(console.log));
subs.add(source$.subscribe(console.log));
subs.add(source$.subscribe(console.log));
setTimeout(() => {
subs.unsubscribe();
}, 5000);
Subscription instance has add method
Special observables
EMPTY
NEVER
throwError
Special observables
EMPTY
Observable that emits no items to the Observer
and immediately emits a complete notification
Special observables
EMPTY
Observable that emits no items to the Observer
and immediately emits a complete notification
interface Action<T =any> {
type: string;
payload?: T;
}
interface Notification {
message: string;
timeout?: number;
}
enum NotificationActionTypes {
Show = '[Notification] Show',
Dispose = '[Notification] Dispose'
}
Special observables
EMPTY
interface Action<T =any> {
type: string;
payload?: T;
}
interface Notification {
message: string;
timeout?: number;
}
enum NotificationActionTypes {
Show = '[Notification] Show',
Dispose = '[Notification] Dispose'
}
const getDelayedDisposeAction = timeout => (
of({ type: NotificationActionTypes.Dispose }).pipe(
delay(timeout)
)
);
const effects$ = actions$.pipe(
switchMap(({ payload: { timeout } }: Action<Notification>) => timeout
? getDelayedDisposeAction(timeout)
: EMPTY)
);
Special observables
Creates an Observable that emits no items to the Observer and immediately emits an error notification
throwError
Special observables
Creates an Observable that emits no items to the Observer and immediately emits an error notification
throwError
const source$ = interval(1000);
const result$ = source$.pipe(
tap(val => {
if(val === 1) {
throw new Error('Bazinga!');
}
}),
catchError(err => {
console.log('Handling error locally');
return throwError(err);
})
);
Rethrow error after local handling
Special observables
Observable that emits no items to the Observer
and never completes
NEVER
Special observables
Observable that emits no items to the Observer
and never completes
NEVER
Place for your example :)
Thanks
for your attention!
wojtrawi@gmail.com
RxJS - tips&tricks
By wojtrawi
RxJS - tips&tricks
- 431