What is Asynchronous JavaScript?

Asynchronous JavaScript allows code to run independently of the main thread, enabling tasks such as API calls, timers, or file reads to execute in the background.

JavaScript is single-threaded, meaning it executes one statement at a time, so asynchronous programming allows time-consuming tasks to execute without blocking other code.

The mechanisms for asynchronous behavior are:

Advertisement
  • Callbacks

  • Promises

  • async/await

Real-life Example

Cooking pasta while boiling water — both tasks run simultaneously without waiting for each other.

Playwright Automation Use Cases

  • Waiting for API responses asynchronously before asserting results (const response = await page.request.get("/api/data");)

  • Performing multiple independent actions without blocking the test flow

  • Handling dynamic page elements with asynchronous waits (page.waitForSelector)

Using async JavaScript ensures efficient, non-blocking automation scripts.


What is a Promise?

A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

It allows handling asynchronous tasks more cleanly than callbacks.

Real-life Example

Ordering food online — you get a promise that your food will arrive (fulfilled) or fail to arrive (rejected).

Real-time Testing Example

In Playwright, waiting for an API response or file download before performing validations, using promises.


The Three States of a Promise

A Promise has three states — Pending, Fulfilled, and Rejected — representing the status of an asynchronous operation at any point.

State Description Example
Pending Initial state; operation not yet completed Waiting for page navigation or an API response
Fulfilled Operation completed successfully; returns a value const data = await response.json();
Rejected Operation failed; returns an error Network error or 404 response

Real-life Example

  • Pending → waiting for food delivery

  • Fulfilled → food delivered successfully

  • Rejected → delivery failed

Playwright Automation Use Cases

  • API testing (wait for an API response with await page.request.get)

  • Dynamic waits (ensure elements exist before interacting)

  • Handling retries or failures cleanly using .then() and .catch()


then, catch, and finally

  • then → executes when a Promise is fulfilled.

  • catch → executes when a Promise is rejected.

  • finally → executes regardless of fulfillment or rejection.

Real-life Example

  • then → food delivered successfully

  • catch → delivery failed

  • finally → leave feedback for the delivery experience

Real-time Testing Example

In Playwright, handling API requests:

  • Process the response with then

  • Handle errors with catch

  • Perform cleanup with finally


async and await

async marks a function to return a Promise and allows the use of await inside it.

await pauses execution of an async function until the Promise is settled (fulfilled or rejected), then returns the result.

async function loginTest() {
  const response = await page.request.get("/api/user");
  const data = await response.json();

  await page.fill("#username", data.username);
  await page.fill("#password", data.password);
  await page.click("#loginButton");
}

Benefits

  • Handle multiple asynchronous operations sequentially or in a readable way.

  • Write cleaner code compared to .then() chaining.

  • Make async automation scripts maintainable.

Real-life Example

Waiting for food delivery (await) inside a kitchen task (async) without stopping other kitchen work.

Quick Summary

async declares a function that returns a Promise; await waits for the Promise to resolve before proceeding, ensuring synchronous-looking code for asynchronous operations.


Why await is Used Inside async Functions

await is used inside async functions to pause execution until a Promise is resolved or rejected, making asynchronous code behave in a synchronous, readable way.

In JavaScript, async functions return Promises; without await, the code does not pause for the Promise to resolve, which can cause problems like acting on data before it arrives or element-not-found errors when interacting too early.

Important

await can only be used inside an async function.

Using it outside one throws a syntax error:

SyntaxError: await is only valid in async function.

Synchronous vs Asynchronous Code

Synchronous code executes line by line, and each statement waits for the previous one to finish (normal function calls).

Asynchronous code executes independently, allowing other tasks to run while a long operation completes (using callbacks, promises, or async/await).

Feature Synchronous Asynchronous
Execution Line by line, blocking Independent, non-blocking
JavaScript mechanism Normal function calls Callbacks, Promises, async/await
Playwright use Simple sequential steps Waiting for API responses, page loads, dynamic elements using await

Real-life Example

  • Synchronous → waiting in a queue one by one.

  • Asynchronous → ordering food online and doing other work while it's prepared.


Error Handling with try-catch and async/await

With async/await, errors from rejected Promises are handled using a try-catch block:

  • The awaited code goes in the try block.

  • If the Promise is rejected (e.g., a network error or a failed assertion), the error is caught in the catch block.

This keeps asynchronous error handling clean and readable, and in Playwright it's used to gracefully handle failed API calls, missing elements, or navigation errors without crashing the whole test.


FAQs

What is asynchronous JavaScript?

Code that runs independently without blocking the single-threaded main thread, using callbacks, promises, or async/await to handle long-running tasks like API calls.

What is a Promise?

An object representing the eventual completion or failure of an asynchronous operation and its resulting value — a cleaner alternative to callbacks.

What are the states of a Promise?

  • Pending (not yet completed)

  • Fulfilled (completed successfully)

  • Rejected (failed)

What do then, catch, and finally do?

  • then runs on fulfillment.

  • catch runs on rejection.

  • finally runs regardless of the outcome.

What is the difference between async and await?

async marks a function that returns a Promise and enables await; await pauses the async function until a Promise settles, then returns its result.

Why must await be used inside an async function?

Because async functions return Promises and only they can pause on await; using await outside one throws:

SyntaxError: await is only valid in async function.

What is the difference between synchronous and asynchronous code?

Synchronous code runs line by line and blocks; asynchronous code runs independently and non-blocking, using callbacks, promises, or async/await.