here is the example
forkJoin(
{
google: ajax.getJSON('https://api.github.com/users/google'),
microsoft: ajax.getJSON('https://api.github.com/users/microsoft'),
users: ajax.getJSON('https://api.github.com/users')
}
)
.subscribe(console.log);
There are three API calls in the forkjoin. I am confused about, is all API calls will run one by one. Or it runs all calls at the same time and waits for responses?
In the scenario you describe, forkJoin acts like Promise.all - all calls are made at the same time (not one by one), once all of them return, forkjoin will fire once with the return value for those calls, here is the definition from the official site:
Accepts an Array of ObservableInput or a dictionary Object of ObservableInput and returns an Observable that emits either an array of values in the exact same order as the passed array, ...
Please note that if one these were an observable (e.g. interval), and it fired three times before the other two return, only the last value would be emitted
See picture below from the official site:
Related
In my application, I need to initiate a burst of long-running http requests which optimally should be allowed to resolve concurrently. The results must be concatenated into a single observable for evaluation downstream.
Merging these observables using mergeMap allows for this concurrency, but this unfortunately does not guarantee order of the results. ConcatMap guarantees order of the values emitted by the output, but it appears that the mapping operation for a given request is not executed until after its predecessor is completed, limiting concurrency to a single request at once (I'm not certain this is how it works but it would explain the result).
Before I start writing a custom operator, I thought I would ask: what is the best way to merge the results from a batch of requests, each returning an observable, while enabling concurrency and guaranteeing order of the results in the output observable?
As the comment said, the forkJoin / combineLatest (you would see the difference between these two by observing their marble diagram), you can get what you want.
How forkJoin works is, it will subscribe to EVERY observables parameter, wait until ALL observables complete, and then emit the value (order is determined by the order of parameter). https://rxjs.dev/api/index/function/forkJoin
And yes, forkJoin is not a pipeable operator, because it's not. It will return an observable. So you can use it with switchMap/other higher order mapping.
Example
const studentsAndTeachers = school$.pipe(
map(school=> school.id),
switchMap(id => forkJoin(http.post('getStudents', id), http.post('getTeachers, id))
)
studentsAndTeachers will get you a tuple ([]) of students and teachers [[students], [teachers]]
I have two observables combined with combineLatest([this.currentPageIndex$, this.currentStoryIndex$]), they represent a shelv with books, first observable emits current book index, second - page of current book.
I have logger service, logging current page and current book number
When page changes everything is fine, but when i switch to a new book both observables emits values: one from current page which become 1 and another is new book number, and logger service logs twice, is there any way to prevent that double logging?
addition, books can have single page
combineLatest works something like:
Wait for all input observable to emit 1 value. After that emit the values.
Every time an observable emits a value, emit that value along with the other values.
So yes, you'll have duplicates there.
You can use concat instead, if you care about the order of the emissions. If not, you can use merge.
I have not been able to find any normative text to answer this question. I have been using this code pattern (this is in TypeScript in an Angular application):
observeSomethingFun$: Observable<Fun>;
...
async loadsOfFun() {
const fun = await this.observeSomethingFun$.toPromise();
// I now have fun
}
In general, Observables need to be unsubscribed from. This happens automatically when the Angular async pipe is used but what happens in this case? Does toPromise unsubscribe after emitting one value?
If not, how do I unsubscribe manually?
Update:
It turns out #Will Taylor's answer below is correct but my question needs some clarification.
In my case the Observable emits a never-ending stream, unlike for example Angular's HttpClient Observables that complete after emitting one value. So
in my case I would never get past the await statement according to Taylor's answer.
RxJS makes this easy to fix. The correct statement turns out to be:
const fun = await this.observeSomethingFun$.pipe(first()).toPromise();
The RxJS first operator will receive the first value and unsubscribe from the source Observable. it will then send out that value to the toPromise operator
and then complete.
No need to unsubscribe.
Which is convenient, as there is no way to unsubscribe from a promise.
If the Observable completes - the promise will resolve with the last value emitted and all subscribers will automatically be unsubscribed at this point.
If the Observable errors - the promise will reject and all subscribers will automatically be unsubscribed at this point.
However, there are a couple of edge cases with toPromise in which the behavior is not completely clear from the docs.
If the Observable emits one or more values but does not complete or error, the promise will neither resolve or reject. In this case, the promise would hang around in memory, which is worth considering when working with toPromise.
If the Observable completes but does not emit a value, the promise will resolve with undefined.
First of all, thank you for this question and answer, I wrongly assumed toPromise() knew what it was doing in all scenarios and would unsubscribe when that observable completes (even if it is an observable stream)
So I will just say that it doesn't hurt to pipe all of your observables before using .toPromise()
I just went through a big ordeal of stepping through our app for memory leaks and found the above answer by Will to be good. The elaboration on the actual question was exactly the same issue I was running into.
We are stepping through each observable in the app right now and we use either
pipe(take(1)) which is equivalent to pipe(first()).
or we use pipe(takeUntil(this.destroyed)) where this.destroyed.next(true) is called when we destroy our particular component or service.
We use take() to keep our verbiage consistent so we can search for take or takeUntil across various components.
Long story short, yeah you might take a very slight performance hit piping your observables at each instance, but I highly recommend doing so in order to prevent any unwanted app-wide memory leak hunts. Then maybe if you have the time you can step through each one and see where .toPromise() actually unsubscribes correctly for you.
Below are the advantages i have read in URL: Angular - Promise vs Observable
promise:
returns a single value
not cancelled
more readable code with try/catch and async/await
observable
works with multiple values over time
cancellable
supports map, filter, reduce and similar operators
use Reactive Extensions (RxJS)
an array whose items arrive asynchronously over time
In observable i see 3 & 4 Point as supports operators and RXJS. I just have a basic question like can't i use RXJS and operators with promises? what is the meaning of point 5
In short, no you can't use those operators (like map, filter) directly on a Promise.
Rxjs does provide an operator toPromise which does convert an Observable to a Promise - if that is your preference.
I think point 5 is actually conflated with point 1. Point 1 is the crux of what Observables are all about: dealing with 0 to n values over time.
You may not think that to be useful if you're used to using Promises simply for Ajax requests - e.g. hit an endpoint and get a value back. But in the case of Observables, you can use them in any context - for example, DOM events.
If you were to create an Observable via listening to a Mouseover event, then you'd be receiving n values over any given length of time - and then you could react to these events.
When thinking in terms of Ajax requests, the classic example is that of the look ahead search input which is detailed in the link of your question.
As #Rich mentioned, rxjs Operators are aimed for continues data streams (e.g. take takes the first n next of an Observable). As such, not all operators are useful for Promise-based results.
However, given that some operators are compact/neat even for Promise, you can use the following:
import { from, firstValueFrom, delay } from "rxjs";
...
// converts to Observable and back to Promise
firstValueFrom(from(myPromise).pipe(delay(1000))
I'm wondering what are the differences between Observable.combineLatest and Observable.forkJoin?
As far as I can see, the only difference is forkJoin expects the Observables to be completed, while combineLatest returns the latest values.
Not only does forkJoin require all input observables to be completed, but it also returns an observable that produces a single value that is an array of the last values produced by the input observables. In other words, it waits until the last input observable completes, and then produces a single value and completes.
In contrast, combineLatest returns an Observable that produces a new value every time the input observables do, once all input observables have produced at least one value. This means it could have infinite values and may not complete. It also means that the input observables don't have to complete before producing a value.
forkJoin - When all observables are completed, emit the last emitted value from each.
combineLatest - When any observable emits a value, emit the latest value from each.
Usage is pretty similar, but you shouldn't forget to unsubscribe from combineLatest unlike forkJoin.
combineLatest(...)
runs observables in parallel, emitting a value each time an observable emits a value after all observables have emitted at least one value.
forkJoin(...)
runs observables in parallel, and emits a single value once all observables have completed.
Consideration for error handling:
If any of the observables error out - with combineLatest it will emit up to the point the error is thrown. forkJoin will just give back an error if any of the observables error out.
Advanced note: CombineLatest doesn't just get a single value for each source and move onto the next. If you need to ensure you only get the 'next available item' for each source observable you can add .pipe(take(1)) to the source observable as you add it to the input array.
There is a situation in Angular which would explain it better. Assume there is a change detection in Angular component, so the latest value is changed. In the pipe and tap methods of combineLatest, the code will be triggered as well. If the latest value is changed N times by the change detection, then the tap methods is also triggered N times as well.