Understanding the results of Promise vs Obervables

Promise:

Implementation

  getDataPromise(): any {
    let promise = new Promise((resolve, reject) => {
      resolve([
        { brand: 'iPhone', model: 'Xmax', price: '$1000' },
        { brand: 'Samsung', model: 'S10', price: '$850' }
      ]);
    });
    return promise;
  }

// promise call
getDataPromise.then((data) => { 
    console.log("Result: ", data);
});

Result is an Array:

Result: [
  { brand: 'iPhone', model: 'Xmax', price: '$1000' },
  { brand: 'Samsung', model: 'S10', price: '$850' }
]

Observable

Implementation

  import { from } from 'rxjs';

  getDataObservable(): any {
    return from([
      { brand: 'iPhone', model: 'Xmax', price: '$1000' },
      { brand: 'Samsung', model: 'S10', price: '$850' }
    ]);
  }

// observable call
getDataObservable().subscribe((data) => {
    console.log("Result: ", data);
});

Result is 2 objects in sequence:

Result: { brand: 'iPhone', model: 'Xmax', price: '$1000' }
Result: { brand: 'Samsung', model: 'S10', price: '$850' }

Was trying to understand the difference, went through online materials but still can not answer the below 2 questions.

  • Why does the result differ ?

  • How to get the result from the observable as an array (similar to as in promise) ?

3 answers

  • answered 2020-01-14 01:34 Adam

    A promise ends - it resolves (then) or rejects (catch) and that's it. An observable streams values until it ends (lots of times, it never ends, you just "lose interest" - or unsubscribe from the stream of values).

    I like the keypress example - if you use a promise to resolve/reject based on a keypress event, you'll only ever listen to "the next" keypress, and then you'll have to create a new promise to listen for the next one and so on.

    With observables, you only ever need to create one and it will just continually stream values to you until you are no longer interested (unsubscribe).

    Promise:
    
    const subscribeToKeyPress = () => {
      let resolve;
      document.addEventListener('keydown',function listener(e) {
          resolve(e);
          // unsubscribe from the event listener, otherwise you've got a memory leak
          document.removeEventListener('keydown',listener);
      });
      return new Promise(r => resolve = r);
    }
    
    subscribeToKeyPress().then(e => {
      // done, never to receive another result
    });
    
    #####
    Observable:
    
    const subscribeToKeyPress$ = () => fromEvent(document,'keypress);
    
    const unsubscribe = subscribeToKeyPress$().subscribe(e => {
      // called continually on each keypress until you unsubscribe
    });
    
    unsubscribe(); // your callback function won't be called anymore, if you don't call unsubscribe when you're done, you've got a memory leak
    
    #####
    Callback Function - the simplest "observable"
    
    const subscribeToKeyPress = callback => {
       const fn = e => callback(e); // this function will get called over and over until you unsubscribe
       document.addEventListener('keypress',fn);
       return () => document.removeEventListener('keypress',fn);
    }
    
    const unsubscribe = subscribeToKeyPress(e => {
      // called continually on each keypress until you unsubscribe
    });
    
    unsubscribe(); // your callback function won't be called anymore, if you don't call unsubscribe when you're done, you've got a memory leak
    

    How to get the result from the observable as an array (similar to as in promise) ?

    Check out https://www.learnrxjs.io/operators/utility/topromise.html

    Also relevant about toPromise: https://github.com/ReactiveX/rxjs/issues/2536

    If your observable doesn't "complete" (like a keypress listener) toPromise will never work (unless you pipe(take(1)) first, but that's a lot for a newbie, no offence, but coming from a promise world, observables are really hard to grok - from my personal experience - until they aren't anymore).

  • answered 2020-01-14 01:47 nopole

    It is just because for from:

    For arrays and iterables, all contained values will be emitted as a sequence!

    so if you make it:

    from([[
      { brand: 'iPhone', model: 'Xmax', price: '$1000' },
      { brand: 'Samsung', model: 'S10', price: '$850' }
    ]]);
    

    then it will be the same result.

    Difference between observable and promise:

    But there is one crucial difference between observable and promise:

    1. if you register an observable after the event, nothing will happen for event that already happened (will only get notified for future events). For a promise, the then() will still take effect.
    2. for observable, you can keep on getting notified for the same type of event. For a promise, once it is settled (resolved or rejected), then it is done, for once only.

  • answered 2020-01-14 01:48 wentjun

    Both promises and observables are asynchronous. Either ways, you will need to handle them respectively

    However, the from operator emits your array as a sequence of values, hence the results that you see.

    As stated on the documentation, the from operator

    Turn an array, promise, or iterable into an observable.

    This is why getDataObservable prints the array of objects as different sequences.

    If you wish to return them as a single sequence, do use the of operator instead.

    Emit variable amount of values in a sequence and then emits a complete notification.

    import { of } from 'rxjs';
    
    const getDataObservable = () => {
      return of([
        { brand: 'iPhone', model: 'Xmax', price: '$1000' },
        { brand: 'Samsung', model: 'S10', price: '$850' }
      ]);
    }
    
    getDataObservable().subscribe((data) => {
        console.log("Result: ", data);
    });
    

    Here is a demo.