1
votes

I am currently migrating to angular 2 from angular 1.x. The application currently follows John Papa's style guide Return a Promise from Data Calls.

activate();

function activate() {
    /**
     * Step 1
     * Ask the getAvengers function for the
     * avenger data and wait for the promise
     */
    return getAvengers().then(function() {
        /**
         * Step 4
         * Perform an action on resolve of final promise
         */
        logger.info('Activated Avengers View');
    });
}

function getAvengers() {
      /**
       * Step 2
       * Ask the data service for the data and wait
       * for the promise
       */
      return dataservice.getAvengers()
          .then(function(data) {
              /**
               * Step 3
               * set the data and resolve the promise
               */
              vm.avengers = data;
              return vm.avengers;
      });
}

How can I do the same thing with Observables? with promise, i can write .then() in multiple functions and return it so that the caller functions also will wait for the promise. But with observable, What is the correct thing to do?
Where exactly i should subscribe and where i should just map the values and return the observable?

3
Yes I have the same problem wile using Observable. - Partha Sarathi Ghosh

3 Answers

2
votes

map should be used when you want to modify the data sent and, by doing so, create a new Observable.

Example:

getAvengers.map(avengers => avengers.forEach(a => a.kill)) will create an observable of dead avengers. Then you can subscribe on this observable to call methods with dead avengers.

subscribe is more like then since it'll simply wait for the data to call the method passed to it. But subscribe will trigger the observable, meaning that if you simply call getAvengers() (assuming that getAvengers returns an Observable) you'll get an Observable, to trigger the request you have to subscribe.

BE CAREFULL:

Calling subscribe twice will trigger the Observable twice, meaning that if you call subscribe on an Observable based on an http request, the request will be done twice.

Therefore you can hendle this using cache with observables, but the documentation will be able to guide you through this.

2
votes

While using promises you use .then(), using Observables, you have plenty of operators that lets you combine multiple observables, create side effect, modify values emitted by the initial observable, etc. map as explained by @Supamiu is an example of all those operators.

Observables do not do anything as-is, they define a data-flow, it's only the subscription that triggers the data-broadcasting process.

Async Pipe

You don't really need to use subscribe most of the time, you can make angular handle that for you, with the async pipe. eg:

component :

this.avengers$=this.dataservice.getAvengers().do(()=>console.log("Avengers fetched");

template :

<span *ngFor="avenger of avengers$|async">{{avenger.name}}</span>

the fact it defines a data-flow allows you to react to events creating very-complex streams without much side effect or efforts.

creating new avengers on click for example:

component :

avengerClick$ = new Subject<mouseEvent>();

constructor(){
    let initialAvengers = this.dataservice.getAvengers().do(()=>console.log("Avengers fetched").switchMap(avangers);
    this.avengers$ = Observable.merge(
        initialAvengers,
        this.avengerClick.switchMap(()=>this.dataservice.createAvenger()) // let's say this.dataservice.createAvenger() returns the new list of avengers
    )
}

template :

<span *ngFor="avenger of avengers$|async">{{avenger.name}}</span>
<span (click)="avengerClick$.next($event)">Create an avenger</span>
1
votes

Assume that your function will invoke multiple functions inside it before doing the http request.

function A() {
  B().subscribe();
}

function B() {
  return C().map();
}

function C() {
  return D().map();
}

function D() {
 // call http reuest and return data
}

The caller function will do the actual subscription, and all the other functions that take part will simply map the data.

Coming to your code, you will have to modify this way :

activate();

    function activate() {
        /**
         * Step 1
         * Ask the getAvengers function for the
         * avenger data and wait for the promise
         */
        return getAvengers().subscribe(
            (data) => {
            // handle your result data
            logger.info('Activated Avengers View');
        });
    }

    function getAvengers() {
          /**
           * Step 2
           * Ask the data service for the data and wait
           * for the promise
           */
          return dataservice.getAvengers()
              .map((data) => {
                  /**
                   * Step 3
                   * set the data and resolve the Observable
                   */
                  vm.avengers = data;
                  return vm.avengers;
          });
    }