One place for hosting & domains

      How To Work With Zip Files in Node.js


      The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.

      Introduction

      Working with files is one of the common tasks among developers. As your files grow in size, they start taking significant space on your hard drive. Sooner or later you may need to transfer the files to other servers or upload multiple files from your local machine to different platforms. Some of these platforms have file size limits, and won’t accept large files. To get around this, you can group the files into a single ZIP file. A ZIP file is an archive format that packs and compresses files with the lossless compression algorithm. The algorithm can reconstruct the data without any data loss. In Node.js, you can use the adm-zip module to create and read ZIP archives.

      In this tutorial, you will use adm-zip module to compress, read, and decompress files. First, you’ll combine multiple files into a ZIP archive using adm-zip. You’ll then list the ZIP archive contents. After that, you’ll add a file to an existing ZIP archive, and then finally, you’ll extract a ZIP archive into a directory.

      Prerequisites

      To follow this tutorial, you’ll need:

      Step 1 — Setting Up the Project

      In this step, you’ll create the directory for your project and install adm-zip as a dependency. This directory is where you’ll keep your program files. You’ll also create another directory containing text files and an image. You’ll archive this directory in the next section.

      Create a directory called zip_app with the following command:

      Navigate into the newly created directory with the cd command:

      Inside the directory, create a package.json file to manage the project dependencies:

      The -y option creates a default package.json file.

      Next, install adm-zip with the npm install command:

      After you run the command, npm will install adm-zip and update the package.json file.

      Next, create a directory called test and move into it:

      In this directory, you will create three text files and download an image. The three files will be filled with dummy content to make their file sizes larger. This will help to demonstrate ZIP compression when you archive this directory.

      Create the file1.txt and fill it with dummy content using the following command:

      • yes "dummy content" | head -n 100000 > file1.txt

      The yes command logs the string dummy content repeatedly. Using the pipe command |, you send the output from the yes command to be used as input for the head command. The head command prints part of the given input into the standard output. The -n option specifies the number of lines that should be written to the standard output. Finally, you redirect the head output to a new file file1.txt using >.

      Create a second file with the string “dummy content” repeated 300,000 lines:

      • yes "dummy content" | head -n 300000 > file2.txt

      Create another file with the dummy content string repeated 600,000 lines:

      • yes "dummy content" | head -n 600000 > file3.txt

      Finally, download an image into the directory using curl:

      • curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/underwater.png

      Move back into the main project directory with the following command:

      The .. will move you to the parent directory, which is zip_app.

      You’ve now created the project directory, installed adm-zip, and created a directory with files for archiving. In the next step, you’ll archive a directory using the adm-zip module.

      Step 2 — Creating a ZIP Archive

      In this step, you’ll use adm-zip to compress and archive the directory you created in the previous section.

      To archive the directory, you’ll import the adm-zip module and use the module’s addLocalFolder() method to add the directory to the adm-zip module’s ZIP object. Afterward, you’ll use the module’s writeZip() method to save the archive in your local system.

      Create and open a new file createArchive.js in your preferred text editor. This tutorial uses nano, a command-line text editor:

      Next, require in the adm-zip module in your createArchive.js file:

      zip_app/createArchive.js

      const AdmZip = require("adm-zip");
      

      The adm-zip module provides a class that contains methods for creating ZIP archives.

      Since it’s common to encounter large files during the archiving process, you might end up blocking the main thread until the ZIP archive is saved. To write non-blocking code, you’ll define an asynchronous function to create and save a ZIP archive.

      In your createArchive.js file, add the following highlighted code:

      zip_app/createArchive.js

      
      const AdmZip = require("adm-zip");
      
      async function createZipArchive() {
        const zip = new AdmZip();
        const outputFile = "test.zip";
        zip.addLocalFolder("./test");
        zip.writeZip(outputFile);
        console.log(`Created ${outputFile} successfully`);
      }
      
      createZipArchive();
      

      createZipArchive is an asynchronous function that creates a ZIP archive from a given directory. What makes it asynchronous is the async keyword you defined before the function label. Within the function, you create an instance of the adm-zip module, which provides methods you can use for reading and creating archives. When you create an instance, adm-zip creates an in-memory ZIP where you can add files or directories.

      Next, you define the archive name and store it in the outputDir variable. To add the test directory to the in-memory archive, you invoke the addLocalFolder() method from adm-zip with the directory path as an argument.

      After the directory is added, you invoke the writeZip() method from adm-zip with a variable containing the name of the ZIP archive. The writeZip() method saves the archive to your local disk.

      Once that’s done, you invoke console.log() to log that the ZIP file has been created successfully.

      Finally, you call the createZipArchive() function.

      Before you run the file, wrap the code in a try…catch block to handle runtime errors:

      zip_app/createArchive.js

      const AdmZip = require("adm-zip");
      
      async function createZipArchive() {
        try {
          const zip = new AdmZip();
          const outputFile = "test.zip";
          zip.addLocalFolder("./test");
          zip.writeZip(outputFile);
          console.log(`Created ${outputFile} successfully`);
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      createZipArchive();
      

      Within the try block, the code will attempt to create a ZIP archive. If successful, the createZipArchive() function will exit, skipping the catch block. If creating a ZIP archive triggers an error, execution will skip to the catch block and log the error in the console.

      Save and exit the file in nano with CTRL+X. Enter y to save the changes, and confirm the file by pressing ENTER on Windows, or the RETURN key on the Mac.

      Run the createArchive.js file using the node command:

      You’ll receive the following output:

      Output

      Created test.zip successfully

      List the directory contents to see if the ZIP archive has been created:

      You’ll receive the following output showing the archive among the contents:

      Output

      createArchive.js node_modules package-lock.json package.json test test.zip

      With the confirmation that the ZIP archive has been created, you’ll compare the ZIP archive, and the test directory file size to see if the compression works.

      Check the test directory size using the du command:

      The -h flag instructs du to show the directory size in a human-readable format.

      After running the command, you will receive the following output:

      Output

      15M test

      Next, check the test.zip archive file size:

      The du command logs the following output:

      Output

      760K test.zip

      As you can see, creating the ZIP file has dropped the directory size from 15 Megabytes(MB) to 760 Kilobytes(KB), which is a huge difference. The ZIP file is more portable and smaller in size.

      Now that you created a ZIP archive, you’re ready to list the contents in a ZIP file.

      Step 3 — Listing Files in a ZIP Archive

      In this step, you’ll read and list all files in a ZIP archive using adm-zip. To do that, you’ll instantiate the adm-zip module with your ZIP archive path. You’ll then call the module’s getEntries() method which returns an array of objects. Each object holds important information about an item in the ZIP archive. To list the files, you’ll iterate over the array and access the filename from the object, and log it in the console.

      Create and open readArchive.js in your favorite text editor:

      In your readArchive.js, add the following code to read and list contents in a ZIP archive:

      zip_app/readArchive.js

      const AdmZip = require("adm-zip");
      
      async function readZipArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
      
          for (const zipEntry of zip.getEntries()) {
            console.log(zipEntry.toString());
          }
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      readZipArchive("./test.zip");
      

      First, you require in the adm-zip module.

      Next, you define the readZipArchive() function, which is an asynchronous function. Within the function, you create an instance of adm-zip with the path of the ZIP file you want to read. The file path is provided by the filepath parameter. adm-zip will read the file and parse it.

      After reading the archive, you define a for....of statement that iterates over objects in an array that the getEntries() method from adm-zip returns when invoked. On each iteration, the object is assigned to the zipEntry variable. Inside the loop, you convert the object into a string that represents the object using the Node.js toString() method, then log it in the console using the console.log() method.

      Finally, you invoke the readZipArchive() function with the ZIP archive file path as an argument.

      Save and exit your file, then run the file with the following command:

      You will get output that resembles the following(edited for brevity):

      Output

      { "entryName": "file1.txt", "name": "file1.txt", "comment": "", "isDirectory": false, "header": { ... }, "compressedData": "<27547 bytes buffer>", "data": "<null>" } ...

      The console will log four objects. The other objects have been edited out to keep the tutorial brief.

      Each file in the archive is represented with an object similar to the one in the preceding output. To get the filename for each file, you need to access the name property.

      In your readArchive.js file, add the following highlighted code to access each filename:

      zip_app/readArchive.js

      const AdmZip = require("adm-zip");
      
      async function readZipArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
      
          for (const zipEntry of zip.getEntries()) {
            console.log(zipEntry.name);
          }
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      readZipArchive("./test.zip");
      

      Save and exit your text editor. Now, run the file again with the node command:

      Running the file results in the following output:

      Output

      file1.txt file2.txt file3.txt underwater.png

      The output now logs the filename of each file in the ZIP archive.

      You can now read and list each file in a ZIP archive. In the next section, you’ll add a file to an existing ZIP archive.

      Step 4 — Adding a File to an Existing Archive

      In this step, you’ll create a file and add it to the ZIP archive you created earlier without extracting it. First, you’ll read the ZIP archive by creating an adm-zip instance. Second, you’ll invoke the module’s addFile() method to add the file in the ZIP. Finally, you’ll save the ZIP archive in the local system.

      Create another file file4.txt with dummy content repeated 600,000 lines:

      • yes "dummy content" | head -n 600000 > file4.txt

      Create and open updateArchive.js in your text editor:

      Require in the adm-zip module and the fs module that allows you to work with files in your updateArchive.js file:

      const AdmZip = require("adm-zip");
      const fs = require("fs").promises;
      

      You require in the promise-based version of the fs module version, which allows you to write asynchronous code. When you invoke an fs method, it will return a promise.

      Next in your updateArchive.js file, add the following highlighted code to add a new file to the ZIP archive:

      zip_app/updateArchive.js

      const AdmZip = require("adm-zip");
      const fs = require("fs").promises;
      
      async function updateZipArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
      
          content = await fs.readFile("./file4.txt");
          zip.addFile("file4.txt", content);
          zip.writeZip(filepath);
          console.log(`Updated ${filepath} successfully`);
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      updateZipArchive("./test.zip");
      

      updateZipArchive is an asynchronous function that reads a file in the filesystem and adds it to an existing ZIP. In the function, you create an instance of adm-zip with the ZIP archive file path in the filepath as a parameter. Next, you invoke the fs module’s readFile() method to read the file in the file system. The readFile() method returns a promise, which you resolve with the await keyword (await is valid in only asynchronous functions). Once resolved, the method returns a buffer object, which contains the file contents.

      Next, you invoke the addFile() method from adm-zip. The method takes two arguments. The first argument is the filename you want to add to the archive, and the second argument is the buffer object containing the contents of the file that the readFile() method reads.

      Afterwards, you invoke adm-zip module’s writeZip() method to save and write new changes in the ZIP archive. Once that’s done, you call the console.log() method to log a success message.

      Finally, you invoke the updateZipArchive() function with the Zip archive file path as an argument.

      Save and exit your file. Run the updateArchive.js file with the following command:

      You’ll see output like this:

      Output

      Updated ./test.zip successfully

      Now, confirm that the ZIP archive contains the new file. Run the readArchive.js file to list the contents in the ZIP archive with the following command:

      You’ll receive the following output:

      file1.txt
      file2.txt
      file3.txt
      file4.txt
      underwater.png
      

      This confirms that the file has been added to the ZIP.

      Now that you can add a file to an existing archive, you’ll extract the archive in the next section.

      In this step, you’ll read and extract all contents in a ZIP archive into a directory. To extract a ZIP archive, you’ll instantiate adm-zip with the archive file path. After that, you’ll invoke the module’s extractAllTo() method with the directory name you want your extracted ZIP contents to reside.

      Create and open extractArchive.js in your text editor:

      Require in the adm-zip module and the path module in your extractArchive.js file:

      zip_app/extractArchive.js

      const AdmZip = require("adm-zip");
      const path = require("path");
      

      The path module provides helpful methods for dealing with file paths.

      Still in your extractArchive.js file, add the following highlighted code to extract an archive:

      zip_app/extractArchive.js

      const AdmZip = require("adm-zip");
      const path = require("path");
      
      async function extractArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
          const outputDir = `${path.parse(filepath).name}_extracted`;
          zip.extractAllTo(outputDir);
      
          console.log(`Extracted to "${outputDir}" successfully`);
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      extractArchive("./test.zip");
      

      extractArchive() is an asynchronous function that takes a parameter containing the file path of the ZIP archive. Within the function, you instantiate adm-zip with the ZIP archive file path provided by the filepath parameter.

      Next, you define a template literal. Inside the template literal placeholder (${}), you invoke the parse() method from the path module with the file path. The parse() method returns an object. To get the name of the ZIP file without the file extension, you append the name property to the object that the parse() method returns. Once the archive name is returned, the template literal interpolates the value with the _extracted string. The value is then stored in the outputDir variable. This will be the name of the extracted directory.

      Next, you invoke adm-zip module’s extractAllTo method with the directory name stored in the outputDir to extract the contents in the directory. After that, you invoke console.log() to log a success message.

      Finally, you call the extractArchive() function with the ZIP archive path.

      Save your file and exit the editor, then run the extractArchive.js file with the following command:

      You receive the following output:

      Output

      Extracted to "test_extracted" successfully

      Confirm that the directory containing the ZIP contents has been created:

      You will receive the following output:

      Output

      createArchive.js file4.txt package-lock.json readArchive.js test.zip updateArchive.js extractArchive.js node_modules package.json test test_extracted

      Now, navigate into the directory containing the extracted contents:

      List the contents in the directory:

      You will receive the following output:

      Output

      file1.txt file2.txt file3.txt file4.txt underwater.png

      You can now see that the directory has all the files that were in the original directory.

      You’ve now extracted the ZIP archive contents into a directory.

      Conclusion

      In this tutorial, you created a ZIP archive, listed its contents, added a new file to the archive, and extracted all of its content into a directory using adm-zip module. This will serve as a good foundation for working with ZIP archives in Node.js.

      To learn more about adm-zip module, view the adm-zip documentation. To continue building your Node.js knowledge, see How To Code in Node.js series



      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