05 Asynchronous JavaScript

ยท

6 min read

05 Asynchronous JavaScript

Syllabus:

1. Introduction to asynchronous programming

2.Callbacks

3.Promises

  • Creating and consuming promises

  • Chaining promises

  • Error handling with promises

4.Async/Await

1. Introduction to asynchronous programming

Asynchronous programming in JavaScript allows tasks to be executed independently of the main program flow.

AspectDescription
Non-BlockingAsynchronous operations do not block the execution of the main program flow, allowing other code to run concurrently.
CallbacksFunctions passed as arguments to handle asynchronous tasks once they are completed.
PromisesIntroduced in ES6, promises represent the eventual completion or failure of an asynchronous operation.
Async/AwaitA syntax introduced later in JavaScript to write asynchronous code that looks synchronous, improving readability.
Event LoopJavaScript's concurrency model that manages the execution of asynchronous tasks via the call stack and task queue.
Common Use CasesHandling tasks like fetching data from servers, interacting with databases, reading files, and non-blocking UI updates.

2.Callbacks

  1. Definition: A callback is a function passed as an argument to another function, which is then invoked or executed inside the outer function to complete some kind of action.

  2. Usage in Asynchronous Operations: In asynchronous operations, callbacks are often used to handle the result of an asynchronous task, such as data retrieval. Once the asynchronous task completes, the callback function is called with the result.

3.Promises

  1. Definition: A promise represents the eventual completion or failure of an asynchronous operation and its resulting value.

  2. States:

    • Pending: Initial state, neither fulfilled nor rejected.

    • Fulfilled (Resolved): The operation completed successfully.

    • Rejected: The operation failed with an error.

  3. Creation: Promises are created using the Promise constructor, which takes a function as an argument, commonly referred to as the executor function. This function receives two parameters: resolve and reject, which are functions used to settle the promise.

const myPromise = new Promise((resolve, reject) => {
  // Asynchronous operation
  if (/* operation successful */) {
    resolve("Operation completed successfully");
  } else {
    reject("Operation failed");
  }
});

Consuming Promises:

  • then(): Used to handle the fulfillment of the promise. It takes two optional callbacks: one for the success case and one for the failure case.

  • catch(): Used to handle promise rejection, similar to the second argument of then().

  • finally(): Executes regardless of the promise state, useful for cleanup tasks.

myPromise
  .then((result) => {
    console.log("Success:", result);
  })
  .catch((error) => {
    console.error("Error:", error);
  })
  .finally(() => {
    console.log("Promise settled");
  });

4.Async/Await

Async Functions:

The async keyword is used to define an async function, which automatically returns a promise. Inside an async function, you can use the await keyword to pause execution until a promise is settled (either resolved or rejected).

async function myAsyncFunction() {
  const result = await someAsyncOperation();
  return result;
}

Await Keyword:

The await keyword can only be used inside async functions. It pauses the execution of the async function until the promise is settled. If the promise is resolved, await returns the resolved value; if the promise is rejected, it throws an error that can be caught using a try...catch block.

async function myAsyncFunction() {
  try {
    const result = await someAsyncOperation();
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

Sequential Execution:

Async/await allows you to write asynchronous code in a more synchronous-looking style, making it easier to understand and maintain. Code written using async/await tends to be more readable than equivalent code written using promises or callbacks.

async function fetchData() {
  const firstData = await fetchFirstData();
  const secondData = await fetchSecondData();
  return [firstData, secondData];
}

Error Handling in async/await:

Error handling in async/await functions can be done using try...catch blocks, which provide a more natural way to handle errors compared to promises.

async function myAsyncFunction() {
  try {
    const result = await someAsyncOperation();
    console.log(result);
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

Parallel Execution:

You can use Promise.all() with async/await to execute multiple asynchronous operations concurrently and await their completion.

async function fetchData() {
  const [firstData, secondData] = await Promise.all([
    fetchFirstData(),
    fetchSecondData(),
  ]);
  return [firstData, secondData];
}

4.Async/Await - ๐Ÿ”— yahoo baba

Synchronous vs Asynchronous Function Declaration and Invocations

// Synchronous function declaration
function test() {
    return 'Hello';
}

// Synchronous function call
console.log(test());

Async Function Declaration and Invocation Comparison

// Asynchronous function declaration using async keyword
async function test() {
    return 'Hello';
}

// Synchronous function call
console.log(test()); // Logs: Promise {<pending>}

// Asynchronous function call using Promise chaining
test().then((result) => {
    console.log(result); // Logs: Hello
})

Various Ways to Define an Async Function

// Async function declaration
async function test() {
    return 'Hello';
}

// Async function expression using function keyword
let test = async function () {
    return 'Hello';
}

// Async arrow function expression with block body
let test = async () => {
    return 'Hello';
}

// Async arrow function expression with implicit return
let test = async () => 'Hello';

Understanding Async Function Execution with Await

let fun = async () => {
    console.log(2);

    // console.log(3);
    await console.log(3);

    console.log(5);
}

console.log(1); // Logs: 1
fun(); // Invokes the async function
console.log(4); // Logs: 4

/* Expected Output:
1
2
3
4
5
*/

Asynchronous Data Fetching with Async/Await

let run = async () => {
    // Fetch JSON data from a URL asynchronously
    let res = await fetch('/05-Asynchronous_JS/data/student.json');
    // Parse JSON response
    let data = await res.json();
    // Log entire data
    console.log(data);
    // Log the first element of data
    console.log(data[0]);
    // Log the age of the first element
    console.log(data[0].age);
    // Return the fetched data
    // return data;

    // SINGLE LINE CODE: Fetch JSON data and directly return it
    return (await fetch('/05-Asynchronous_JS/data/student.json')).json();
}
// FileStudent data.json 
[
    {
        "name": "Ayush Kumar",
        "age": 19,
        "city": "BH"        
    },
    {
        "name": "Rahul",
        "age": 15,
        "city": "BPL"        
    },
    {
        "name": "Ajay",
        "age": 21,
        "city": "Motihari"        
    }
]
// Invoke the asynchronous function
let r = run();
// Log the promise returned by the async function
console.log(r);

/* Expected Output:
Promise {<pending>}
[ { name: 'John', age: 25 }, { name: 'Alice', age: 30 }, { name: 'Bob', age: 28 } ]
{ name: 'John', age: 25 }
25
*/
// Execute the asynchronous function and handle its returned promise
run()
    .then((res) => {
        // Log the resolved data
        console.log(res);
        // Log the third element of the resolved data
        console.log(res[2]);
    })
    // Handle errors if any
    .catch((err) => console.log(err));

Handling Errors with Async/Await and Promise Chaining

let sun = async () => {
    try {
        // Fetch JSON data from a URL asynchronously
        let res = await fetch('/05-Asynchronous_JS/data/student.json');
        // Parse JSON response
        let data = await res.json();
        // Log entire data
        console.log(data);
        // Log the first element of data
        console.log(data[0]);
        // Log the age of the first element
        console.log(data[0].age);
        // Return the fetched data
        return data;
    } catch (error) {
        // Handle errors
        console.log('Error!', error);
    }
}

// Execute the asynchronous function and handle its returned promise
sun()
    .then((res) => {
        // Log the resolved data
        console.log("Using .then method : ", res);
        // Log the third element of the resolved data
        console.log(res[2]);
    });
ย