One place for hosting & domains

      starting at only

      $3.96/Year

      JavaScript Promises for Dummies


      Javascript Promises are not difficult. However, lots of people find it a little bit hard to understand at the beginning. Therefore, I would like to write down the way I understand promises, in a dummy way.

      Understanding Promises

      A Promise in short:

      “Imagine you are a kid. Your mom promises you that she’ll get you a new phone next week.”

      You don’t know if you will get that phone until next week. Your mom can either really buy you a brand new phone, or she doesn’t, because she is not happy :(.

      That is a promise. A promise has 3 states. They are:

      1. Pending: You don’t know if you will get that phone
      2. Fulfilled: Mom is happy, she buys you a brand new phone
      3. Rejected: Mom is unhappy, she doesn’t buy you a phone

      Creating a Promise

      Let’s convert this to JavaScript.

      // ES5: Part 1
      
      var isMomHappy = false;
      
      // Promise
      var willIGetNewPhone = new Promise(
          function (resolve, reject) {
              if (isMomHappy) {
                  var phone = {
                      brand: 'Samsung',
                      color: 'black'
                  };
                  resolve(phone); // fulfilled
              } else {
                  var reason = new Error('mom is not happy');
                  reject(reason); // reject
              }
      
          }
      );
      

      The code is quite expressive in itself.

      Below is how a promise syntax look like normally:

      // promise syntax look like this
      new Promise(function (resolve, reject) { ... } );
      

      Consuming Promises

      Now that we have the promise, let’s consume it.

      // ES5: Part 2
      
      var willIGetNewPhone = ... // continue from part 1
      
      // call our promise
      var askMom = function () {
          willIGetNewPhone
              .then(function (fulfilled) {
                  // yay, you got a new phone
                  console.log(fulfilled);
                   // output: { brand: 'Samsung', color: 'black' }
              })
              .catch(function (error) {
                  // oops, mom don't buy it
                  console.log(error.message);
                   // output: 'mom is not happy'
              });
      };
      
      askMom();
      

      Let’s run the example and see the result!

      Demo: https://jsbin.com/nifocu/1/edit?js,console

      Result

      Chaining Promises

      Promises are chainable.

      Let’s say, you, the kid, promises your friend that you will show them the new phone when your mom buy you one.

      That is another promise. Let’s write it!

      // ES5
      
      // 2nd promise
      var showOff = function (phone) {
          return new Promise(
              function (resolve, reject) {
                  var message="Hey friend, I have a new " +
                      phone.color + ' ' + phone.brand + ' phone';
      
                  resolve(message);
              }
          );
      };
      

      Notes: We can shorthen the above code by written as below:

      // shorten it
      
      // 2nd promise
      var showOff = function (phone) {
          var message="Hey friend, I have a new " +
                      phone.color + ' ' + phone.brand + ' phone';
      
          return Promise.resolve(message);
      };
      

      Let’s chain the promises. You, the kid can only start the showOff promise after the willIGetNewPhone promise.

      // call our promise
      var askMom = function () {
          willIGetNewPhone
          .then(showOff) // chain it here
          .then(function (fulfilled) {
                  console.log(fulfilled);
               // output: 'Hey friend, I have a new black Samsung phone.'
              })
              .catch(function (error) {
                  // oops, mom don't buy it
                  console.log(error.message);
               // output: 'mom is not happy'
              });
      };
      

      That’s how easy to chain the promise.

      Promises are Asynchronous

      Promises are asynchronous. Let’s log a message before and after we call the promise.

      // call our promise
      var askMom = function () {
          console.log('before asking Mom'); // log before
          willIGetNewPhone
              .then(showOff)
              .then(function (fulfilled) {
                  console.log(fulfilled);
              })
              .catch(function (error) {
                  console.log(error.message);
              });
          console.log('after asking mom'); // log after
      }
      

      What is the sequence of expected output? Probably you expect:

      1. before asking Mom
      2. Hey friend, I have a new black Samsung phone.
      3. after asking mom
      

      However, the actual output sequence is:

      1. before asking Mom
      2. after asking mom
      3. Hey friend, I have a new black Samsung phone.
      

      Don’t believe me? Go try it yourself: https://jsbin.com/vepateq/edit?js,console !

      Output

      Why? Because life (or JS) waits for no man.

      You, the kid, wouldn’t stop playing while waiting for your mom promise (the new phone). Don’t you? That’s something we call asynchronous, the code will run without blocking or waiting for the result. Anything that need to wait for promise to proceed, you put that in .then.

      Here is the full example in ES5.

      // ES5: Full example
      
      var isMomHappy = true;
      
      // Promise
      var willIGetNewPhone = new Promise(
          function (resolve, reject) {
              if (isMomHappy) {
                  var phone = {
                      brand: 'Samsung',
                      color: 'black'
                  };
                  resolve(phone); // fulfilled
              } else {
                  var reason = new Error('mom is not happy');
                  reject(reason); // reject
              }
      
          }
      );
      
      // 2nd promise
      var showOff = function (phone) {
          var message="Hey friend, I have a new " +
                      phone.color + ' ' + phone.brand + ' phone';
      
          return Promise.resolve(message);
      };
      
      // call our promise
      var askMom = function () {
          willIGetNewPhone
          .then(showOff) // chain it here
          .then(function (fulfilled) {
                  console.log(fulfilled);
                  // output: 'Hey friend, I have a new black Samsung phone.'
              })
              .catch(function (error) {
                  // oops, mom don't buy it
                  console.log(error.message);
                  // output: 'mom is not happy'
              });
      };
      
      askMom();
      

      Promises in ES5, ES6/2015, ES7/Next

      ES5 – Majority browsers

      The demo code is workable in ES5 environments (all major browsers + NodeJs) if you include Bluebird promise library. It’s because ES5 doesn’t support promises out of the box. Another famous promise library is Q by Kris Kowal.

      ES6 / ES2015 – Modern browsers, NodeJs v6

      The demo code works out of the box because ES6 supports promises natively. In addition, with ES6 functions, we can further simplify the code with fat arrow => and use const and let.

      Here is the full example in ES6 code:

      //_ ES6: Full example_
      
      const isMomHappy = true;
      
      // Promise
      const willIGetNewPhone = new Promise(
          (resolve, reject) => { // fat arrow
              if (isMomHappy) {
                  const phone = {
                      brand: 'Samsung',
                      color: 'black'
                  };
                  resolve(phone);
              } else {
                  const reason = new Error('mom is not happy');
                  reject(reason);
              }
      
          }
      );
      
      // 2nd promise
      const showOff = function (phone) {
          const message="Hey friend, I have a new " +
                      phone.color + ' ' + phone.brand + ' phone';
          return Promise.resolve(message);
      };
      
      // call our promise
      const askMom = function () {
          willIGetNewPhone
              .then(showOff)
              .then(fulfilled => console.log(fulfilled)) // fat arrow
              .catch(error => console.log(error.message)); // fat arrow
      };
      
      askMom();
      

      Notes that all the var are replaced with const. All the function(resolve, reject) has been simplified to (resolve, reject) =>. There are a few benefits come with these changes. Read more on:-

      ES7 – Async Await make the syntax look prettier

      ES7 introduce async and await syntax. It makes the asynchronous syntax look prettier and easier to understand, without the .then and .catch.

      Rewrite our example with ES7 syntax.

      // ES7: Full example
      const isMomHappy = true;
      
      // Promise
      const willIGetNewPhone = new Promise(
          (resolve, reject) => {
              if (isMomHappy) {
                  const phone = {
                      brand: 'Samsung',
                      color: 'black'
                  };
                  resolve(phone);
              } else {
                  const reason = new Error('mom is not happy');
                  reject(reason);
              }
      
          }
      );
      
      // 2nd promise
      async function showOff(phone) {
          return new Promise(
              (resolve, reject) => {
                  var message="Hey friend, I have a new " +
                      phone.color + ' ' + phone.brand + ' phone';
      
                  resolve(message);
              }
          );
      };
      
      // call our promise in ES7 async await style
      async function askMom() {
          try {
              console.log('before asking Mom');
      
              let phone = await willIGetNewPhone;
              let message = await showOff(phone);
      
              console.log(message);
              console.log('after asking mom');
          }
          catch (error) {
              console.log(error.message);
          }
      }
      
      // async await it here too
      (async () => {
          await askMom();
      })();
      

      Why Promises and When to Use Them?

      Why do we need promises? How’s the world look like before promise? Before answering these questions, let’s go back to the fundamental.

      Normal Function vs Async Function

      Let’s take a look at these two example, both example perform addition of two number, one add using normal function, the other add remotely.

      Normal Function to Add Two Numbers

      // add two numbers normally
      
      function add (num1, num2) {
          return num1 + num2;
      }
      
      const result = add(1, 2); // you get result = 3 immediately
      
      Async Function to Add Two numbers
      // add two numbers remotely
      
      // get the result by calling an API
      const result = getAddResultFromServer('http://www.example.com?num1=1&num2=2');
      // you get result  = "undefined"
      

      If you add the numbers with normal function, you get the result immediately. However when you issue a remote call to get result, you need to wait, you can’t get the result immediately.

      Or put it this way, you don’t know if you will get the result because the server might be down, slow in response, etc. You don’t want your entire process to be blocked while waiting for the result.

      Calling APIs, downloading files, reading files are among some of the usual async operations that you’ll perform.

      World Before Promises: Callback

      Must we use promise for asynchronous call? Nope. Prior to Promise, we use callback. Callback is just a function you call when you get the return result. Let’s modify the previous example to accept a callback.

      // add two numbers remotely
      // get the result by calling an API
      
      function addAsync (num1, num2, callback) {
          // use the famous jQuery getJSON callback API
          return $.getJSON('http://www.example.com', {
              num1: num1,
              num2: num2
          }, callback);
      }
      
      addAsync(1, 2, success => {
          // callback
          const result = success; // you get result = 3 here
      });
      

      The syntax looks ok, why do we need promises then?

      What if You Want to Perform Subsequent Async Action?

      Let’s say, instead of just add the numbers one time, we want to add 3 times. In a normal function, we do this:-

      // add two numbers normally
      
      let resultA, resultB, resultC;
      
       function add (num1, num2) {
          return num1 + num2;
      }
      
      resultA = add(1, 2); // you get resultA = 3 immediately
      resultB = add(resultA, 3); // you get resultB = 6 immediately
      resultC = add(resultB, 4); // you get resultC = 10 immediately
      
      console.log('total' + resultC);
      console.log(resultA, resultB, resultC);
      

      How it looks like with callbacks?

      // add two numbers remotely
      // get the result by calling an API
      
      let resultA, resultB, resultC;
      
      function addAsync (num1, num2, callback) {
          // use the famous jQuery getJSON callback API
          // https://api.jquery.com/jQuery.getJSON/
          return $.getJSON('http://www.example.com', {
              num1: num1,
              num2: num2
          }, callback);
      }
      
      addAsync(1, 2, success => {
          // callback 1
          resultA = success; // you get result = 3 here
      
          addAsync(resultA, 3, success => {
              // callback 2
              resultB = success; // you get result = 6 here
      
              addAsync(resultB, 4, success => {
                  // callback 3
                  resultC = success; // you get result = 10 here
      
                  console.log('total' + resultC);
                  console.log(resultA, resultB, resultC);
              });
          });
      });
      

      Demo: https://jsbin.com/barimo/edit?html,js,console

      The syntax is less user friendly. In a nicer term, It looks like a pyramid, but people usually refer this as “callback hell”, because the callback nested into another callback. Imagine you have 10 callbacks, your code will nested 10 times!

      Escape From Callback Hell

      Promises come in to rescue. Let’s look at the promise version of the same example.

      // add two numbers remotely using observable
      
      let resultA, resultB, resultC;
      
      function addAsync(num1, num2) {
          // use ES6 fetch API, which return a promise
          // What is .json()? https://developer.mozilla.org/en-US/docs/Web/API/Body/json
          return fetch(`http://www.example.com?num1=${num1}&num2=${num2}`)
              .then(x => x.json()); 
      }
      
      addAsync(1, 2)
          .then(success => {
              resultA = success;
              return resultA;
          })
          .then(success => addAsync(success, 3))
          .then(success => {
              resultB = success;
              return resultB;
          })
          .then(success => addAsync(success, 4))
          .then(success => {
              resultC = success;
              return resultC;
          })
          .then(success => {
              console.log('total: ' + success)
              console.log(resultA, resultB, resultC)
          });
      

      Demo: https://jsbin.com/qafane/edit?js,console

      With promises, we flatten the callback with .then. In a way, it looks cleaner because of no callback nesting. Of course, with ES7 async syntax, we can even further enhance this example, but I leave that to you. 🙂

      New Kid On the Block: Observables

      Before you settle down with promises, there is something that has come about to make it even easier to deal with async data called Observables.

      Observables are lazy event streams which can emit zero or more events, and may or may not finish.source

      Some key differences between promises and observable are:

      Fear not, let look at the same demo written with Observables. In this example, I am using RxJS for the observables.

      let Observable = Rx.Observable;
      let resultA, resultB, resultC;
      
      function addAsync(num1, num2) {
          // use ES6 fetch API, which return a promise
          const promise = fetch(`http://www.example.com?num1=${num1}&num2=${num2}`)
              .then(x => x.json());
      
          return Observable.fromPromise(promise);
      }
      
      addAsync(1,2)
        .do(x => resultA = x)
        .flatMap(x => addAsync(x, 3))
        .do(x => resultB = x)
        .flatMap(x => addAsync(x, 4))
        .do(x => resultC = x)
        .subscribe(x => {
          console.log('total: ' + x)
          console.log(resultA, resultB, resultC)
        });
      

      Demo: https://jsbin.com/dosaviwalu/edit?js,console

      Notes:

      Observables can do more funky stuff easily. For example, delay add function by 3 seconds with just one line of code or retry so you can retry a call a certain number of times.

      ...
      
      addAsync(1,2)
        .delay(3000) // delay 3 seconds
        .do(x => resultA = x)
        ...
      

      Well, let’s talk about Observables in future post. (You may read one of my RxJs post here too!)

      Summary

      Get yourself familiar with callbacks and promises. Understand them and use them. Don’t worry about Observables, just yet. All three can factor into your development depending on the situation.

      That’s it. Hopefully this article smoothen your path to tame the JavaScript promises. Happy coding!



      Source link

      RxJS Operators for Dummies: forkJoin, zip, combineLatest, withLatestFrom


      If you are confused about the differences between forkJoin, zip, combineLatest and withLatestFrom, you are not alone! 🙂

      These 4 operators are what we know as combination operators – we use them when we need to join information from multiple observables.

      Which operator should I use?

      That is what this article is for! We will talk about the usage and differences between these 4 operators in an easy to understand way, so you know which one to choose when the time comes.

      Setup

      Imagine you are printing t-shirts. Ms. Color holds the color information and Mr. Logo holds the logo information. Both of them will pick color and logo spontaneously. You will need to wait and combine these two information continuously in order to print t-shirts. Ms. Color and Mr. Logo represent two observables in our code – color$ and logo$.

      you, ms. color & mr. logo

      // 0. Import Rxjs operators
      import { forkJoin, zip, combineLatest, Subject } from 'rxjs';
      import { withLatestFrom, take, first } from 'rxjs/operators';
      
      // 1. Define shirt color and logo options
      type Color="white" | 'green' | 'red' | 'blue';
      type Logo = 'fish' | 'dog' | 'bird' | 'cow';
      
      // 2. Create the two persons - color and logo observables, 
      // They will communicate with us later (when we subscribe)
      const color$ = new Subject<Color>();
      const logo$ = new Subject<Logo>();
      
      // 3. We are ready to start printing shirt. Need to subscribe to color and logo observables to produce shirts, we will write code here later
      ...
      
      // 4. The two persons(observables) are doing their job, picking color and logo
      color$.next('white');
      logo$.next('fish');
      
      color$.next('green');
      logo$.next('dog');
      
      color$.next('red');
      logo$.next('bird');
      
      color$.next('blue');
      
      // 5. When the two persons(observables) has no more info, they said bye bye.. We will write code here later
      ...
      

      I guess the code above is pretty expressive itself. We created two observables by using Subject. For part 4 in the code, every .next(<value>) means Ms. Color or Mr. Logo is picking color or logo.

      Take note of the sequence of information (part 4 in our code), here is the summary:-

      sequence of info

      1. Ms. Color picks WHITE
      2. Mr. Logo picks FISH
      3. Ms. Color picks GREEN
      4. Mr. Logo picks DOG
      5. Ms. Color picks RED
      6. Mr. Logo picks BIRD
      7. Ms. Color picks BLUE
      

      Later, we will update our code (part 3 & 5) to subscribe to both color and logo observables using the 4 different operators to see how the shirts are produced differently.

      All set. Let’s start exploring our first operator!

      zip – the love birds operator

      I call zip operator the love birds operator. Love birds need to always be together. Remember Titanic, the “you jump, I jump” type.

      Let’s replace our code (part 3) with below:

      // 3. We are ready to start printing shirt...
      zip(color$, logo$)
          .subscribe(([color, logo]) => console.log(`${color} shirt with ${logo}`));
      

      TL;DR

      For those of you who are not familar with JavaScript ES6/ES2015 destructuring assignment, you might find the syntax in subscribe [color, logo] a little bit odd.

      When we zip color$ and logo$, we expect to receive an array of 2 items during subscribe, first item is color and second is logo (follow their orders in zip function).

      The traditional way of writing it would be .subscribe((data) => console.log(${data[0]} shirt with ${data[1]})). As you can see, it’s not very obvious that data[0] is color.

      ES6 allows us to unpack the value from arrays. Therefore, we unpack data into [color, logo] straight away. More readable right?

      Result

      Alright, let’s go back to our code and run it. The shirt printing result would be:-

      zip - printed shirts

      Here is what get to log in the console:

      1. white shirt with fish
      2. green shirt with dog
      3. red shirt with bird
      

      How does zip work?

      Again, zip operator is the love birds operator. In our case, color will wait for logo whenever there are new value (vice versa). Both values must change then only the log gets triggered.

      1. Ms. Color picks WHITE
      2. Mr. Logo picks FISH <- log 01, WHITE + FISH in pair, love birds!
      3. Ms. Color picks GREEN
      4. Mr. Logo picks DOG <- log 02, GREEN + DOG in pair, love birds!
      5. Ms. Color picks RED
      6. Mr. Logo picks BIRD <- log 03, RED + BIRD in pair love birds!
      7. Ms. Color picks BLUE <- waiting for love...
      

      zip operator can accept more than 2 observables – no matter how many observables, they must all wait for each other, no man left behind!

      combineLatest – the go dutch operator

      I call combineLatest operator the go dutch operator. They are independent and doesn’t wait for each other, they take care of themselves.

      Let’s replace the setup code part 3 with the below code:

      // 3. We are ready to start printing shirt...
      combineLatest(color$, logo$)
          .subscribe(([color, logo]) => console.log(`${color} shirt with ${logo}`));
      

      The shirt printing result would be:-

      combinedLatest - printed shirts

      Here is what get to log in the console:

      1. white shirt with fish
      2. green shirt with fish
      3. green shirt with dog
      4. red shirt with dog
      5. red shirt with bird
      6. blue shirt with bird
      

      How does combineLatest work?

      As mentioned, combineLatest is the go dutch operator – once they meet their mates one time, they will wait for no man. In our case, first function is triggered after both color and logo values change. There onwards, either color or logo value changed will trigger the log.

      1. Ms. Color picks WHITE 
      2. Mr. Logo picks FISH <- log 01, color + logo first meet, let's go dutch!
      3. Ms. Color picks GREEN <- log 02, GREEN + FISH
      4. Mr. Logo picks DOG <- log 03, DOG + GREEN
      5. Ms. Color picks RED <- log 04, RED + DOG
      6. Mr. Logo picks BIRD <- log 05 BIRD + RED 
      7. Ms. Color picks BLUE <- log 06 BLUE + BIRD
      

      withLatestFrom – the master slave operator

      I call withLatestFrom operator the master slave operator. At first, master must meet the slave. After that, the master will take the lead, giving command. The slave will just follow and has no voice. 🙁

      Let’s replace the setup code part 3 with the below code:

      // 3. We are ready to start printing shirt...
      color$.pipe(withLatestFrom(logo$))
          .subscribe(([color, logo]) => console.log(`${color} shirt with ${logo}`));
      

      The shirt printing result would be:-

      withLatestFrom - printed shirts

      Here is what get to log in the console:

      1. green shirt with fish
      2. red shirt with dog
      3. blue shirt with bird
      

      How does withLatestFrom work?

      Can you guess who is the master and who is the slave in our case?

      You guessed it! color is the master while logo is the slave. At first (only once), color(master) will look for logo(slave). Once the logo(slave) has responded, color(master) will take the lead. Log will get triggered whenever the next color(master) value is changed. The logo(slave) value changes will not trigger the console log.

      1. Ms. Color picks WHITE <- nothing happen, waiting for slave
      2. Mr. Logo picks FISH <- slave found, wait for the master's command
      3. Ms. Color picks GREEN <- log 01, master says GREEN! So, GREEN + FISH
      4. Mr. Logo picks DOG
      5. Ms. Color picks RED <- log 02, master says RED! So, RED + DOG
      6. Mr. Logo picks BIRD
      7. Ms. Color picks BLUE <- log 03 master says BLUE! So, BLUE + BIRD
      

      forkJoin – the final destination operator

      Definitely not the horror movie) kind of final destination! I call forkJoin operator the final destination operator because they are very serious, they only commit once all parties are very sure that they are completely true, final destination of each other.

      Let’s replace the setup code part 3 with the below code:

      // 3. We are ready to start printing shirt...
      forkJoin(color$, logo$)
          .subscribe(([color, logo]) => console.log(`${color} shirt with ${logo}`));
      

      The shirt printing result would be:-
      forkJoin - printed shirts

      You see it right, the result is NOTHING! There is no log in console.

      How does forkJoin work?

      forkJoin is the final destination operator! They are very serious to make sure each other are their final destination. In our code, both color and logo observables are not complete, we can keep pushing value by calling .next – that means they are not serious enough and thus they are not final destination of each other.

      So, how do we be serious?

      We need to complete both observables. Let’s replace our setup code part 5 with the below:

      // 5. When the two persons(observables) ...
      color$.complete();
      logo$.complete();
      

      Great! With the above code changes, Here is our shirt printing result:-

      forkJoin (complete) - printed shirts

      Here is what get to log in the console:

      1. blue shirt with bird
      

      Here is the sequence of when the log happens:-

      1. Ms. Color picks WHITE
      2. Mr. Logo picks FISH
      3. Ms. Color picks GREEN
      4. Mr. Logo picks DOG
      5. Ms. Color picks RED
      6. Mr. Logo picks BIRD
      7. Ms. Color picks BLUE
      8. Ms. Color completed <-- color is serious!
      9. Mr. Logo completed <--- log no 01, both logo & color are completed. Final destination!
      

      There is more than one way to complete observable. There are operators that allow you to auto complete observable when conditions met, for example take, takeUntil, first.

      Let’s say, you only want to make 1 shirt, you only need to know the first color and logo, In this case, you don’t care about the rest of the info that Ms. Color & Mr. Logo provide. You can make use of take or first operator to achieve auto complete observable once first color and logo emit.

      Let’s replace the setup code part 3 with the below code:

      // 3. We are ready to start printing shirt...
      const firstColor$ = color$.pipe(take(1));
      const firstLogo$ = logo$.pipe(first());
      
      forkJoin(firstColor$, firstLogo$)
          .subscribe(([color, logo]) => console.log(`${color} shirt with ${logo}`));
      

      You can remove all the code in part 5 as well, we don’t need the two lines .complete() (as previous code) because take and first will auto complete the observable when the condition met.

      With the above change, you should see a white shirt with fish!

      forkjoin (auto complete) - printed shirtst

      Summary

      Phew~ this is a pretty long article huh? Here is the summary of all results.
      one page answer

      Let’s wrap up! In summary, these 4 operators trigger the next action (subscribe function in our case) in slightly different conditions:

      • zip – the love birds, always work as a team, triggers only when all observables return new values
      • combineLatest – the go dutch, start trigger once all observables return new values, then wait for no man, trigger every time when either observable return new value.
      • withLatestFrom – the master slave, master first waits for slave, after that, action get triggered every time only when master return new value.
      • forkJoin – the final destination, trigger once when all observables have completed.

      Which operator should I use?

      So I guess you can answer “which operator should I use?” better now. As a general rule of thumb – choose the one that works for you. In some cases, the outcome of using different operators might be the same (that’s why people get confused on which one to use), it would be good to understand the intention of the operator & decide accordingly.

      One of the most common use case of combination operators would be calling a few apis, wait for all results return, then executing next logic. Either forkJoin or zip will work and return same result because api calls are one-time only, auto-completed once result is returned (e.g. Angular httpClient.get).

      However, by understanding the operators more, forkJoin might be more suitable in this case. It is because we “seriously” want to wait for all http responses to complete before proceed to the next step. zip is intended for observables with multiple emits. In our case, we expect only one emit for each http request. Therefore, I think forkJoin is more appropriate (oh well, either way, your code will run just fine & return the same result, but it’s good to know right?).

      Demo

      Alright, here is the final code. Please note that the code is a little bit different from demo because I include the code to draw the UI. Logic and general code structure stay the same though.

      See the Demo on Stackblitz

      Quiz!

      As a bonus, let me give you a quiz!

      quiz

      Figure out the correct results with the provided code. Feel free to play around with it, and explore different scenarios (add more more observables maybe?)!

      Quiz Answer: Click here for the quiz’s answer! (please try yourself first)

      That’s all. Happy coding!



      Source link