1

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.

1

4 Answers 4

4

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
})
Sign up to request clarification or add additional context in comments.

2 Comments

@dextercom Although it works, it has some limitations : forkJoin works only with observables that have finished emitting (such as HTTP calls), and it does not allow one to listen to every observable separately. Keep those informations in mind when using it, it might save you hours of debugging and head scratching !
@trichetriche Thanks, for my application its just what I need. But I will heed your warning.
2

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')
  )

Comments

0

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.

6 Comments

I would add that await needs to be used within an async function. In that respect you might as well use a normal function and just return Promise.all(promises). It comes down to the same thing.
Going from Observables to Promises is a step back. You don't need to do that if you just want to make sequenced calls.
Combine Promises with Observables is not step back. As you can see when you use promises your code is simpler and less nested (looks like synch code) - this is why I choose this approach.
It is indeed a step back. Promises can't do what observables do (like cancelling for instance) and they add unecessary complexity (async/await is pretty new, not that many people know how to use it, even you made a mistake for the await keyword). As for the nested argument, your code is as nested as any observable, you simply omitted the then keyword. Other than that, you're free to choose whatever approach you feel like using, But I maintain my statement : if you're using observables and go back to promises, you make a step back.
@trichetriche You say that using new JS keywords is step back? In terms of further results processing my code is flat, other solutions are nested. In my code, below ; you can process loaded result directly - in other solutions there is used subscribe which required to put processing code in function (so no directly - and this cause that code is more nested). I dont say that Promieses are better/worse than Observables - but in this case combine botch is better. I don't see my mistake in await keyword - can you point where it is exactly?
|
0

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')
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.