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

  • 421