How do i know who called RXJS MergeMap response - javascript

So i merge various Observables into one with mergemap, then i subscribe to said merged Observable and i get various responses. But they are anonymous; the response only says "SUCCESS" but i want to know the id that made that response
const requests = from(
this.array.map((e)=>{
return this.eventsService.acceptId(e.id);
})
).pipe(mergeMap( x => x ));
requests.subscribe(
(res)=>{
console.log(res)
}
)
this works as intended but i want to know the id that caused the response inside the subscribe.
the calls need to be parallel, with individual API calls

You can map the ID's in as part of the originoal observables, before merging themall together.
That might look like this:
const requests = merge(
this.array.map(
({id}) => this.eventsService.acceptId(id).pipe(
map(payload => ({payload, id}))
)
)
);
requests.subscribe(
(res) => {
console.log(res)
}
);

Related

Return a tuple for a forkJoin rxJS

I have an array of requests I'd like to execute in parallel with forkJoin and then mapped to the result set where the first item is the original object and the second item is the result of the request. For example:
const deletionRequests = users.map(user => this.userService.deleteUser(user.id));
forkJoin(...deletionRequests)
.subscribe(results => console.log(results);
Currently, results only holds a boolean value but I'd like to instead receive a [User, Result] object instead so that I can use this to modify my templates list of users.
As writ forkJoin() should be return an array of boolean values which are in the same order as the array of users.
results[n] is the result for user[n]
That being said, what you want might be closer to
const deletionRequests = users.map(user =>
this.userService.deleteUser(user.id).pipe(
map(result => ([user, result]))
)
);
This doesn't change forkJoin at all, it just alters your array of streams such they they now output a tuple. This might be good if you want to perform extra processing on the users. For example, filtering out bad usernames:
const deletionRequests = users
.filter(user => isFilteredName(user.name))
.map(user =>
this.userService.deleteUser(user.id).pipe(
map(result => ([user, result]))
)
);
Now users and results don't line up after a forkJoin, so the extra processing to map your stream to a tuple is necessary.

Performing a dynamic quantity of https synchronously using Observable

I have an array of URL's for which i need to perform a http.get sequentially. And, depending on the result, carry on to the next item in the array.
For example:
var myArray = this.myService.getUrls();
// myArray can be something like this:
// ['http://www.us.someurl.com', 'http://www.hk.anotherurl.com']
// Or...
// ['http://www.us.randomurl.com']
// Or...
// ['http://www.us.someurl.com', 'http://www.hk.notaurl.com', 'http://www.pl.someurl.com', 'http://www.in.boringurl.com]
When i perform a http.get on each of these, my server will respond within its data something along the line of {isReady: true} or {isReady: false}
What i want to be able to do it for each item in myArray, perform the http.get, and if the result of that individual call is isReady = true, dont perform the next http.get in the array. If isReady = false, then move on to the next URL in the array.
I have tried flatMap, but its seems i cannot get around hard coding the URLs:
var myArray = ['http://www.us.someurl.com', 'http://www.hk.anotherurl.com'];
this.http.get('http://www.us.someurl.com')
.map(res => res.json())
.flatMap(group => this.http.get('http://www.hk.anotherurl.com') )
.map(res => res.json())
.subscribe((res) => {
console.log(res);
});
});
You can achieve it using a combination of concatMap, filter and take:
const URLS = ['http://www.us.someurl.com', 'http://www.hk.anotherurl.com'];
// ...
public getResponse()
{
return from(URLS).pipe(
concatMap(url => this.http.get<{ isReady: boolean }>(url)),
filter(({ isReady }) => isReady),
take(1),
);
}
DEMO

Loop over typescript Array asynchronously

I have a typescript array this.products
I need to loop over the elements of the array and for each element send parameters to Angular service which makes an API call and gets an answer to the client as an Observable. However, due to asynchronous nature of Observable, my loop finishes before all of the answer are sent back from the server.
This is my code:
this.products.forEeach((ele, idx) => {
this.myService.getSomeDetails(ele.prop1, ele.prop2).subscribe(result => {
// do something with result
});
});
I need for the loop to advance only after the completion of each observable subscription. How can I implement it? Thanks.
What you are looking for is forkJoin:
https://rxjs-dev.firebaseapp.com/api/index/function/forkJoin
Map your array of items to an array of api call observables, and pass them into forkJoin. This will emit an array of all your resolved api calls.
Quick and dirty example:
forkJoin(this.products.map(i => this.myService.getSomeDetails(ele.prop1, ele.prop2))).subscribe(arrayOfApiCallResults => {
// get results from arrayOfApiCallResults
})
You don't need async/await keywords to make your call in sequence.
import { concat } from 'rxjs';
concat(this.products.map(ele => this.myService.getSomeDetails(ele.prop1, ele.prop2)))
.subscribe(
response => console.log(response),
error => console.log(error),
() => console.log('all calls done')
)
Try this:
let results = await Promise.all(this.products.map(ele =>
this.myService.getSomeDetails(ele.prop1, ele.prop2).toPromise()
));
// put code to process results here
Requests are sent parallel. Rememeber to add async keyword in function definition which use above code. More info here.
This is good way to code it.
from(this.products).pipe(mergeMap(
ele => this.myService.getSomeDetails(ele.prop1, ele.prop2)
)).subscribe(result => {
() => console.log('.'),
e => console.error(e),
() => console.log('Complete')
});

How to combine the results of two observable in angular?

How to combine the results of two observable in angular?
this.http.get(url1)
.map((res: Response) => res.json())
.subscribe((data1: any) => {
this.data1 = data1;
});
this.http.get(url2)
.map((res: Response) => res.json())
.subscribe((data2: any) => {
this.data2 = data2;
});
toDisplay(){
// logic about combining this.data1 and this.data2;
}
The above is wrong, because we couldn't get data1 and data2 immediately.
this.http.get(url1)
.map((res: Response) => res.json())
.subscribe((data1: any) => {
this.http.get(url2)
.map((res: Response) => res.json())
.subscribe((data2: any) => {
this.data2 = data2;
// logic about combining this.data1 and this.data2
// and set to this.data;
this.toDisplay();
});
});
toDisplay(){
// display data
// this.data;
}
I can combine the results in the subscribe method of the second observable.
But I'm not sure if it's a good practice to achieve my requirement.
Update:
Another way I found is using forkJoin to combine the results and return a new observable.
let o1: Observable<any> = this.http.get(url1)
.map((res: Response) => res.json())
let o2: Observable<any> = this.http.get(url2)
.map((res: Response) => res.json());
Observable.forkJoin(o1, o2)
.subscribe(val => { // [data1, data2]
// logic about combining data1 and data2;
toDisplay(); // display data
});
toDisplay(){
//
}
A great way to do this is to use the rxjs forkjoin operator (which is included with Angular btw), this keeps you away from nested async function hell where you have to nest function after function using the callbacks.
Here's a great tutorial on how to use forkjoin (and more):
https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs
In the example you make two http requests and then in the subscribe fat arrow function the response is an array of the results that you can then bring together as you see fit:
let character = this.http.get('https://swapi.co/api/people/1').map(res => res.json());
let characterHomeworld = this.http.get('http://swapi.co/api/planets/1').map(res => res.json());
Observable.forkJoin([character, characterHomeworld]).subscribe(results => {
// results[0] is our character
// results[1] is our character homeworld
results[0].homeworld = results[1];
this.loadedCharacter = results[0];
});
The first element in the array always corresponds to the first http request you pass in, and so on. I used this successfully a few days ago with four simultaneous requests and it worked perfectly.
TRY with forkJoin if it's not working then give this a try combineLatest()
What it do - it combine the last emitted value from your stream array into one before completion of your stream array.
Observable.combineLatest(
this.filesServiceOberserval,
this.filesServiceOberserval,
this.processesServiceOberserval,
).subscribe(
data => {
this.inputs = data[0];
this.outputs = data[1];
this.processes = data[2];
},
err => console.error(err)
);
We can combine observables in different ways based on our need. I had two problems:
The response of first is the input for the second one: flatMap() is
suitable in this case.
Both must finish before proceeding further: forkJoin()/megre()/concat() can be used depending on how you want
your output.
You can find details of all the above functions here.
You can find even more operations that can be performed to combine observables here.
You can merge multiple observables into a single observable and then reduce the values from the source observable into a single value.
const cats = this.http.get<Pet[]>('https://example.com/cats.json');
const dogs = this.http.get<Pet[]>('https://example.com/dogs.json');
const catsAndDogs = merge(cats, dogs).pipe(reduce((a, b) => a.concat(b)));
You can also use mergemap which merges two obserables
Merge map documentation

Passing composite data in rxjs observable chains

I have a block of code where I'm calling observables in a chain like so:
getData().flatMap(results => {
return callNextDataMethod(results);
}
.flatMap(results2 => {
// next operation and so forth
})
Now, I understand that flatMap will allow me to pass the results of the previous observable to the next one. However what I need is to both do that as well as pass the results on the first. Let's assume that I do some cleanup, validation, etc on the data that comes back in getData and I want that passed to all flatMap calls down the chain. Is there an operator in rxjs that will do this for me?
Thanks
You can use a map operator to combine the argument received by the flatMap projection function with the observable's result:
getData()
.flatMap(data =>
getMoreData(data).map(moreData => ({ data, moreData }))
)
.flatMap(({ data, moreData }) =>
getEvenMoreData(moreData).map(evenMoreData => ({ data, moreData, evenMoreData }))
)
.flatMap(({ data, moreData, evenMoreData }) =>
...

Categories

Resources