Advanced JavaScript Design Patterns: Asynchronous Programming and Event Handling

Shantun Parmar
4 min readMay 21, 2024

--

Are you one of those who are confused and wondering whether JavaScript is asynchronous or synchronous? The lines between these concepts may not be clear, even for experienced developers.

In this article I explain the principles behind running JavaScript, followed by a example of synchronous and asynchronous. JavaScript. Next, I’ll take a closer look at the different asynchronous patterns in JS.

JavaScript Execution

JavaScript uses just-in-time system (JIT) compilation method. In the first pass, memory is reserved for variables and functions. In subsequent runs, the code is processed sequentially. This makes JavaScript a combination of interpretation and compilation, not just one or the other.

When you execute JavaScript code, the execution context is formed globally, which consists of the following: two steps. : Creation and execution.

  • The creation phase allocates memory by looking for variables and functions in the code.
  • The execution phase involves the actual execution of the code.

The commands are executed individually; The next line does not continue until the current line is completed.

  • The code runs sequentially with a single main thread.
  • Long function calls (for example, network requests) can block the main thread and prevent it from responding.

How to get the asynchronous programming inclusions

Asynchronous programming is fundamental to modern web development. This is facilitated by the JavaScript event loop, a fundamental concept that enables non-blocking behavior.

The event loop

Think about JavaScript how the chef manages orders in a crowded kitchen. Instead of preparing one dish after another, the chef changes tasks and serves everyone faster. Likewise, the JavaScript event loop processes multiple tasks, thus avoiding conflicts.

The event loop allows JavaScript to execute tasks simultaneously without blocking the main thread. This is essential for time-consuming operations, such as network requests, timers and interactions, and ensures that the application remains responsive.

Here is an overview of how the event loop works:

  • Execution stack: the functions are executed one at a time and form a stack.
  • Message queue: asynchronous tasks (events, Web APIs) waiting for execution.
  • Event loop: monitors the stack and fetches tasks one by one from the queue when the stack is empty.
  • Running tasks: tasks are executed one by one one in order and are completed before the next. begins.
  • Asynchronous: non-blocking operations (callbacks, promises) allow scheduling
  • Completion: Once the asynchronous tasks complete, their results are added to the queue.
  • The event loop continues: the queue is flushed to the stack and the flow stops interrupts.
  • Responsive applications: Enables multitasking and responsiveness in applications.

Effectively managing asynchronous behavior is critical to avoiding memory overhead due to multiple concurrent operations.

Control techniques for asynchronous operations:

  • Callback: fundamentals for asynchronous processing, so be careful callbacks are hell.
  • Promise: structured approach, cascading operations, use of .then() and .catch().
  • Async/Await: concise ES2017 syntax, asynchronous functions, await promises.

Generators and iterators: interruptible functions, asynchronous iteration using generators and iterators.

What are generators and iterators?

Generators and iterators are powerful models asynchronous in JavaScript that helps Manage asynchronous control flow and handle asynchronous operations in a more readable and maintainable way. Let’s look at each concept individually.

Generators
Generators represent a class of functions that can be stopped and restarted. They are declared with the syntax “function*”. In a generating function, you can use the “yield” keyword to stop the execution of the function and produce a value for the caller. The function can then pick up where it left off.

Example of generate function:

// Function to generate an array of random numbers within a given range
function generate(length, min, max) {
const randomNumbers = [];
for (let i = 0; i < length; i++) {
const randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
randomNumbers.push(randomNumber);
}
return randomNumbers;
}

// Example usage
const arrayLength = 10;
const minNumber = 1;
const maxNumber = 100;
const randomArray = generate(arrayLength, minNumber, maxNumber);
console.log("Random Array:", randomArray);

Constructors are particularly useful for asynchronous operations because they allow you to suspend execution while waiting for asynchronous tasks to complete and then resume execution when the tasks complete.

Iterators
An iterator is an object that defines a sequence and a way to access it. In JavaScript, iterators are created using the Symbol.iterator method. An iterator must implement a “next()” method that returns an object with two properties: “value”, the current value of the sequence, and “done”, which indicates whether the sequence is complete.

Here is a simple example of an iterator implementation:

// Define an iterable object
const iterableObject = {
// Array of items
items: ['apple', 'banana', 'cherry'],

// Custom iterator method
[Symbol.iterator]: function() {
let index = 0;

// Iterator object with next() method
return {
next: () => {
if (index < this.items.length) {
// Return current item and move to the next index
return { value: this.items[index++], done: false };
} else {
// Signal end of iteration
return { done: true };
}
}
};
}
};

// Iterate over the iterable object using a for...of loop
for (const item of iterableObject) {
console.log(item);
}

Generators can be used to create iterators for simplicity, especially when has to do with asynchronous operations. By using the “yield” keyword in a generator, you can easily generate values in a controlled and asynchronous manner.

Here is an example of an asynchronous iterator based on a generator. :

// Asynchronous generator function
async function* asyncNumberGenerator() {
let i = 0;
while (true) {
// Await an asynchronous function call to generate the next value
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulating an asynchronous operation
yield i++;
}
}

// Example usage of the asynchronous iterator
(async () => {
// Create an asynchronous iterator
const iterator = asyncNumberGenerator();

// Use a loop to iterate over the asynchronous values
for await (const value of iterator) {
console.log(value); // Print each asynchronous value
if (value >= 5) {
// Break the loop after 5 values
break;
}
}
})();

In this example, “asyncNumberGenerator” creates values that await an asynchronous function call.

--

--

Shantun Parmar

Software Engineer | Full Stack Developer | Python | JavaScript | Problem Solver https://bit.ly/3V9HoR9