Mastering JavaScript: Get Started with Synchronous and Asynchronous Concepts

manish sonimanish soni
8 min read

Hello fellow developers, this is the first blog in a series of advanced web development blogs designed to provide practice with a gradual learning curve through practical examples.

Basics to keep in mind

  1. JavaScript is an interpreted language, executed line-by-line at runtime, which simplifies execution but can lead to performance overhead and runtime errors.

  2. It is dynamically typed, allowing variables to change types during execution, unlike statically typed languages.

  3. JavaScript operates in a single-threaded environment, processing one task at a time.

  4. It features automatic garbage collection, managing memory allocation and deallocation to prevent memory leaks.

  5. While beginner-friendly, JavaScript faces performance challenges compared to languages like C++ and Rust.

  6. Efforts like Bun aim to address JavaScript's performance issues, but more progress is needed.

Learn basic syntax up to loops on your own using W3Schools.

Got it! Let’s first explain 3 key theoretical concepts that are foundational to solving the problem statements. After that, I’ll provide problem statements for you to solve based on these concepts.


Functions

A function is a reusable block of code that performs a specific task. It can take inputs (called parameters), process them, and return an output. Functions are essential for organising code, avoiding repetition, and making programs modular.

Key Features:

  1. Declaration: Use the function keyword to define a function.

     function greet(name) {
         return "Hello, " + name;
     }
    
  2. Parameters: Functions can accept inputs (parameters) to customize their behaviour.

     function add(a, b) {
         return a + b;
     }
    
  3. Return Statement: Functions can return a value using the return keyword.

     function isAdult(age) {
         return age >= 18;
     }
    
  4. Arrow Functions: A shorter syntax for writing functions.

     const greet = (name) => "Hello, " + name;
    

Use Case:

Functions are used everywhere in programming, from simple calculations to complex algorithms. For example, a function can validate user input, perform calculations, or manipulate data.


Arrays and Array Methods

An array is a collection of elements stored in a single variable. Arrays are ordered, and each element can be accessed using its index (starting from 0).

Key Features:

  1. Declaration:

     const fruits = ["apple", "banana", "orange"];
    
  2. Accessing Elements:

     console.log(fruits[0]); // Output: "apple"
    
  3. Common Array Methods:

    • push(): Adds an element to the end of the array.

        fruits.push("mango");
      
    • filter(): Creates a new array with elements that pass a test.

        const filteredFruits = fruits.filter(fruit => fruit.length > 5);
      
    • map(): Creates a new array by transforming each element.

        const upperCaseFruits = fruits.map(fruit => fruit.toUpperCase());
      
    • reduce(): Reduces the array to a single value (e.g., sum of all elements).

        const numbers = [1, 2, 3, 4];
        const sum = numbers.reduce((acc, num) => acc + num, 0);
      

Use Case:

Arrays are used to store lists of data, such as user records, product inventories, or even coordinates. Array methods like filter, map, and reduce are powerful tools for processing and transforming data.


Objects and Nested Objects

An object is a collection of key-value pairs, where each key is a string (or symbol) and each value can be any data type (e.g., string, number, array, or even another object).

Key Features:

  1. Declaration:

     const user = {
         name: "Harkirat",
         age: 21,
         address: {
             city: "Delhi",
             country: "India"
         }
     };
    
  2. Accessing Properties:

     console.log(user.name); // Output: "Harkirat"
     console.log(user.address.city); // Output: "Delhi"
    
  3. Adding/Updating Properties:

     user.gender = "male"; // Add a new property
     user.age = 22; // Update an existing property
    
  4. Iterating Over Properties:

     for (let key in user) {
         console.log(key + ": " + user[key]);
     }
    

Use Case:

Objects are used to represent real-world entities, such as users, products, or orders. Nested objects allow you to model complex data structures, such as a user’s address or a product’s details.


Don't overlook problem statements.

Problem Statements to Solve

Now that you understand the theoretical concepts, here are some problem statements for you to solve:


Problem Statement 1: Dynamic Greeting System

Write a function that takes a user object (with name, role, and loginTime properties) and returns a greeting based on the time of login and user role. Use the rules provided in the earlier problem statement.


Problem Statement 2: Voting Eligibility with Age and Nationality

Write a function that takes a user object (with name, age, nationality, and votingCountry properties) and returns a message indicating whether the user is eligible to vote in the specified country.


Problem Statement 3: Filter and Transform Array of Objects

Given an array of user objects, write a function that:

  1. Filters out users who are below 18 years old.

  2. Transforms the remaining users into a new array containing only their name and age.

  3. Sorts the transformed array by age in ascending order.


Write a function that takes a nested object and a key as input. The function should search for the key in the nested object and return its value. If the key is not found, return "Key not found".


Problem Statement 5: Advanced Filtering with Multiple Conditions

Given an array of user objects, write a function that:

  1. Filters users who are above 18 years old and have a subscription status of "active".

  2. Calculates the average age of the filtered users.

  3. Returns the average age rounded to 2 decimal places.


These problem statements are designed to test your understanding of functions, arrays, and objects. Try solving them step by step, and let me know if you need hints or explanations!

Synchronous JS

Synchronous code is executed line by line, in the order it's written. Each operation waits for the previous one to complete before moving on to the next one.

For example

function sum(n) {
    let ans = 0;
    for (let i = 1; i <= n; i++) {
        ans = ans + i
    }
    return ans;
}

const ans1 = sum(100);
console.log(ans1);
const ans2 = sum(1000);
console.log(ans2);
const ans3 = sum(10000);
console.log(ans3);

Prerequisite for Async functions

I/O operations, really?

  • Refers to operations in computer programs involving significant data transfer or read/write tasks.

  • Includes tasks related to disks, networks, or databases.

  • These tasks take more time compared to in-memory computations.


CPU-Bound Tasks

  • Definition: Limited by CPU speed and power.

  • Characteristics:

    • Heavy computation (e.g., loops, math operations).

    • High CPU utilisation.

    • Blocking in single-threaded environments.

  • Example:

      let sum = 0;
      for (let i = 1; i <= 1000000; i++) sum += i;
      console.log(sum);
    
  • Real-World Analogy: Running for 3 miles (constant effort).


I/O-Bound Tasks

  • Definition: Limited by input/output operations (e.g., disk, network).

  • Characteristics:

    • Waiting for I/O (e.g., file read, network request).

    • Low CPU utilization.

    • Non-blocking with async handling.

  • Example:

      const fs = require("fs");
      fs.readFile("a.txt", "utf-8", (err, data) => console.log(data));
    
  • Real-World Analogy: Boiling water (waiting, minimal effort).


Asynchronous JavaScript

  • Purpose: Handle I/O-bound tasks efficiently.

  • Tools:

    • Callbacks: Functions executed after async operations.

    • Promises: Handle async operations with .then() and .catch().

    • Async/Await: Syntactic sugar for promises.

  • Example:

      fs.readFile("a.txt", "utf-8", (err, data) => {
          if (err) console.error(err);
          else console.log(data);
      });
    

Key Differences

FeatureCPU-Bound TasksI/O-Bound Tasks
BottleneckCPU speedI/O device speed
CPU UsageHighLow
BlockingYesNo (with async)
ExamplesLoops, calculationsFile I/O, network requests
AnalogyRunning for 3 milesBoiling water

Problem Statement

  1. CPU-Bound: Calculate factorial of 100,000 using a loop. Measure execution time.

  2. I/O-Bound: Read a large file asynchronously. Measure execution time.

Example:

// CPU-Bound
function factorial(n) {
    let result = 1n;
    for (let i = 2n; i <= n; i++) result *= i;
    return result;
}
console.time("Factorial");
factorial(100000n);
console.timeEnd("Factorial");

// I/O-Bound
const fs = require("fs");
console.time("File Read");
fs.readFile("largeFile.txt", "utf-8", (err, data) => {
    if (err) console.error(err);
    else console.timeEnd("File Read");
});

Require/import statements

Require/import statements are used in JavaScript to include external modules or libraries in your code. The require function is commonly used in Node.js to load modules, while import statements are part of the ES6 module system and are used in modern JavaScript environments. These statements allow you to access functions, objects, or variables from other files, facilitating code modularity and reuse.


What is synchronous? Isn't it interesting to explore contradictions and learn synchronously?

It just means that we know JavaScript is single-threaded, so isn't it true that we can't run two lines of code together? It will also be true in async; it means more than one event will not be initiated at any time. Only after one event is complete will the next be initiated.

Example :-

console.log("Sync Start");
for (let i = 0; i < 3; i++) console.log(i); // Blocks
console.log("Sync End");

console.log("Async Start");
setTimeout(() => console.log("Timeout"), 0);
console.log("Async End");

Output:

Sync Start
0
1
2
Sync End
Async Start
Async End
Timeout

In the example above, you may notice that the setTimeout function executes immediately after it is called because the time is set to 0 milliseconds. However, the synchronous code blocks it, which is why "Async End" appears before "Timeout." This behaviour is due to JavaScript's single-threaded nature. If we place synchronous code after the asynchronous setTimeout function, it can delay the execution of the asynchronous functionality.

Sync = blocking; Async = non-blocking via event loop. JavaScript is single-threaded but handles async efficiently.

Stay tuned! The next blog will be coming next weekend.

2
Subscribe to my newsletter

Read articles from manish soni directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

manish soni
manish soni

Just exploring development technologies.