RxJS построена вокруг работы с Observable. Там огромное количество методов, потому что потоки значений можно создавать из очень разных источников и способов комбинирования этих потоков также много.

Я бы хотел привести вот такой простой пример и отметить интересные моменты:

import { Observable} from 'rxjs/Observable';
import { Subject } from 'rxjs/Rx';
import "rxjs/add/observable/zip";

const a$ = new Subject<number>();
const b$ = new Subject<number>();

const c$ = Observable.zip(a$, b$, (a, b) => a + b)
    .subscribe((x) => console.log(x));

b$.next(1);
a$.next(1);

Мне здесь кажется интересным то, что мы объявляем потоки событий a$ и b$. Это может быть, что угодно и появляется откуда угодно. Здесь для простоты это потоки чисел. Далее мы объявляем c$ и определяем, что он получается из a$ и b$ оператором zip. У него есть своя особая семантика. И теперь c$ связан с a$, b$ причем у нас нет значений пока для этих потоков чисел. И только когда появятся значения:

b$.next(1);
a$.next(1);

В консоль будет выведено значение для c$.

Вот так, не используя никаких примитивов синхронизации мы получили, что-то похожее на барьер.