The RxJS operators combineLatest and withLatestFrom both merge observables, but they decide differently when to emit. On a project that leaned heavily on RxJS I had to merge two observables where one emitted far more often than the other, and the operator I picked changed the output more than any code around it.
The rule is short. combineLatest emits every time any of its sources emits. withLatestFrom emits only when the observable you pipe from emits, and it reads the last value the other source produced. The RxJS docs have marble diagrams for both operators if you want the visual version. Here are the two contexts I was working in.
combineLatest emits whenever either observable emitsA live dashboard shows the latest stock price next to the user's account balance and calculates a potential investment return from the pair. The stock price refreshes every second. The account balance changes only when the user makes a transaction.
stockPriceObservable: emits the latest stock price.accountBalanceObservable: emits the latest account balance.One subscription covers both sources:
Rx.combineLatest([stockPriceObservable, accountBalanceObservable]).subscribe(
([latestStockPrice, currentAccountBalance]) => {
const potentialReturn = calculatePotentialReturn(
latestStockPrice,
currentAccountBalance,
);
updateDashboard(potentialReturn);
},
);The dashboard has to show the newest figure from either source. combineLatest emits a fresh pair whenever the stock price or the balance changes, so both numbers on screen stay current.
withLatestFrom emits only when the trigger observable emitsAn order screen applies the current promo code discount when the user clicks "Place Order". The discount has to be read at the moment of the click, and a new promo code on its own must not place an order.
placeOrderClickObservable: emits when the "Place Order" button is clicked.promoCodeDiscountObservable: emits the current discount percentage when a new promo code is activated.The click observable is the one you pipe from, so it drives every emission:
placeOrderClickObservable
.pipe(Rx.withLatestFrom(promoCodeDiscountObservable))
.subscribe(([clickEvent, latestPromoDiscount]) => {
applyDiscount(latestPromoDiscount);
placeOrder();
});The user's click is the event that matters here. At that moment we need the latest promo code discount to apply to the order. The discount observable only supplies a value; it never triggers the order placement logic.
Neither operator waits for its sources to complete. If you need a single emission after every source finishes, for example a set of parallel HTTP requests, forkJoin is the operator for that job. Otherwise pick by the trigger: if any source should push an update, use combineLatest; if one source is the trigger and the rest are context, use withLatestFrom.
Occasional notes on software, tools, and things I learn. No spam.
Unsubscribe anytime.