33

I am trying to improve my knowledge of Angular2 by migrating an application currently written in Angular1/AngularJS.

One feature in particular has me stumped. I am trying to replicate a feature where a calling function waits to proceed until the function it is calling has completed a loop of promises. In AngularJS, the function I am calling looks basically like this:

this.processStuff = function( inputarray, parentnodeid ) {
    var promises = [];
    var _self = this;
    angular.forEach( inputarray , function( nodeid ) {

        switch ( parentnodeid )
        {
            case ‘AAA’ : var component = _self.doMoreStuff( nodeid, parentnodeid ); break;
            case ‘BBB’ : var component = _self.doMoreStuff( nodeid, parentnodeid ); break;
            case ‘CCC’ : var component = _self.doMoreStuff( nodeid, parentnodeid ); break;
            default    : var component = null;
        }
        promises.push( component );
    });
    return $q.all(promises);
}; 

It contains a forEach loop that calls another function (doMoreStuff) that also returns a promise, and it stores all those returned promises in an array.

With AngularJS, when I call processStuff in another function, I could count on the system waiting until processStuff completed before entering into the code in the then block:

service.processStuff( arraying, parentidarg )
       .then(function( data ) {
              ... 

The caller of processStuff waits for all doMoreStuff invocations to complete until the caller of processStuff enters into its then block.

I am unsure of how to achieve this with Angular2 and Observables. I can see from these posts that to mimic promises, Observables basically just use subscribe() instead of then():

Angular 1.x $q to Angular 2.0 beta

But how do I await all invocations in the forEach loop to complete before my application can proceed?

Tony Brasunas
  • 4,012
  • 3
  • 39
  • 51
Benjamin McFerren
  • 822
  • 5
  • 21
  • 36

2 Answers2

74

I have been doing this with forkJoin

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';

Observable.forkJoin(
  this.http.get('./friends.json').map((res: Response) => res.json()),
  this.http.get('./customer.json').map((res: Response) => res.json())
)
.subscribe(res => this.combined = {friends: res[0].friends, customer: res[1]});

Some more info here: http://www.syntaxsuccess.com/viewarticle/angular-2.0-and-http

TGH
  • 38,769
  • 12
  • 102
  • 135
  • 2
    the argument for the forkJoin can be an array? Could you please show an example where you are iterating with a for loop to populate this "array". I am not certain if the loop is inside or outside the new Observable block and how to use observer.next(); and observer.complete(); – Benjamin McFerren Feb 25 '16 at 06:18
  • see here for specific import instructions: http://stackoverflow.com/questions/34581471/angular2-http-is-missing-map-function/34581771#34581771 – Benjamin McFerren Feb 27 '16 at 23:27
  • 2
    Yes, it can be an array: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/forkjoin.md – msanford Oct 21 '16 at 15:08
  • 1
    also: import 'rxjs/add/observable/forkJoin' – kravits88 Feb 13 '17 at 09:18
  • 1
    Why did the Rx team choose the name `forkJoin`? Why not call it something descriptive like `with` or `when`? `forkJoin` is not clear what it does – Luke T O'Brien Apr 12 '17 at 11:45
  • you can look up the fork/join model. – knt5784 Jul 11 '19 at 19:22
4

In RxJS v6 and later you can do this more eloquently with zip.

    import { zip } from 'rxjs';

    const promise1 = yourSvc.get(yourFavoriteAPI.endpoint1);
    const promise2 = yourSvc.get(yourFavoriteAPI.endpoint2);

    const promises = zip(promise1, promise2);

    promises.subscribe(([data1, data2]) => {
      console.log(data1);
      console.log(data2);
    });

While the result is the same, I find zip preferable to forkJoin since zip is more universal and can handle new values from the observables.

Details from the rxjs documentation:

The zip operator will subscribe to all inner observables, waiting for each to emit a value. Once this occurs, all values with the corresponding index will be emitted. This will continue until at least one inner observable completes.

Tony Brasunas
  • 4,012
  • 3
  • 39
  • 51