≡ Menu

In the world of JavaScript, especially when dealing with operations that might take some time (like fetching data from an API, reading files, or setting timers), the concept of asynchronous programming is paramount.

It allows our applications to remain responsive even when performing long-running tasks.

Two powerful keywords that have revolutionized asynchronous JavaScript are async and await.

Today, let’s dive into a practical example of how async/await can be used to execute a series of asynchronous tasks sequentially, ensuring that each task completes before the next one begins.

We’ll dissect a concise piece of code that elegantly achieves this.

The executeInSeries Function

 

Imagine you have a list of operations that need to be performed in a specific order, and some of these operations are asynchronous (meaning they might take some time to finish). The executeInSeries function provides a neat solution:

/**
 * Executes a series of asynchronous tasks one after another.
 * @param {Array<Function>} tasks - An array of functions, each returning a Promise.
 * @returns {Promise<Array>} A promise that resolves with an array of results from each task.
 */
async function executeInSeries(tasks) {
  const results = [];
  for (const task of tasks) {
    try {
      const result = await task();
      results.push(result);
    } catch (error) {
      console.error('Task failed:', error);
      // Pushing the error object allows the series to continue.
      // You could also re-throw the error to stop the execution.
      results.push(error); 
    }
  }
  return results;
}

Let’s break down what’s happening here:

  1. async function executeInSeries(tasks): The async keyword declares this function as asynchronous. This means it will always return a Promise, and we can use the await keyword inside it. The function takes an array tasks as input, where each element is expected to be a function that returns a Promise.
  2. for (const task of tasks) { ... }: We loop through each task in the provided tasks array.
  3. const result = await task();: This is where the magic of await happens. The await keyword pauses the execution of the executeInSeries function until the Promise returned by task() either resolves (completes successfully) or rejects (encounters an error). This guarantees that each task finishes before the next one starts.
  4. try...catch: This block is crucial for handling potential errors. If a Promise returned by a task rejects, the catch block will gracefully handle the error, preventing the entire program from crashing.

Seeing It in Action: A Simple Example

Let’s create a couple of asynchronous “tasks” using setTimeout to simulate delays. We’ll use a Promise to represent an operation that will complete in the future.

// Task 1: A function that returns a Promise resolving after 100ms
const task1 = () => new Promise(resolve => setTimeout(() => resolve('Task 1 done'), 100));

// Task 2: A function that returns a Promise resolving after 50ms
const task2 = () => new Promise(resolve => setTimeout(() => resolve('Task 2 done'), 50));

// Execute the tasks in series
executeInSeries([task1, task2])
  .then(results => console.log('Series results:', results));

Understanding the task Function

The task1 function is a great example of how to wrap a delayed operation in a Promise:

const task1 = () => new Promise(resolve => setTimeout(() => resolve('Task 1 done'), 100));

This code snippet does the following:

  • It defines an arrow function task1.
  • When task1() is called, it immediately returns a new Promise.
  • Inside the Promise’s executor, setTimeout is called. It schedules a callback function to run after 100 milliseconds.
  • After the 100ms delay, the callback executes and calls resolve('Task 1 done'), which fulfills the Promise.

The Execution Flow

When we run executeInSeries([task1, task2]), the program follows a clear, sequential path:

  1. The for loop begins with task1. await task1() is called, and the executeInSeries function pauses for 100ms.
  2. After 100ms, task1‘s Promise resolves, and 'Task 1 done' is added to the results array.
  3. The loop moves to task2. await task2() is called, and the function pauses for another 50ms.
  4. After 50ms, task2‘s Promise resolves, and 'Task 2 done' is added to the results array.
  5. The loop finishes, and the executeInSeries Promise resolves with the final array of results: ['Task 1 done', 'Task 2 done'].

This ensures that even though task2 has a shorter delay, it will always be executed after task1, producing a predictable and ordered output.

Why is This Useful?

Executing asynchronous tasks in series is crucial in scenarios where the outcome of one task depends on the completion of a previous one. For example:

  • Database Operations: Writing data to one table and then using the ID from that new record to write data to another table.
  • API Chains: Fetching a user ID from one API endpoint and then using that ID to retrieve the user’s profile from a second endpoint.
  • File Processing: Reading a file, processing its contents, and then writing the modified data to a new file.

The async/await syntax makes this kind of sequential asynchronous logic much cleaner and easier to read compared to traditional Promise chaining with .then().

By combining these keywords with a simple loop, we can build robust and predictable asynchronous workflows.

{ 0 comments }

If you want to set yourself up for success as a software engineer avoid making these 5 mistakes.

Mistake #1 – Not mastering the fundamentals. To master the fundamentals of web programming you need to be proficient in HTML,CSS, Javascript & Python.

You can’t just know the basics (functions,variables,loops,etc) when it comes to these programming languages.

You need to be able to use these programming languages to build real world applications that solve real world problems.

Mistake #2 – Not having a really good understanding of data structures and algorithms.

If you want to work for a big tech company like Facebook,Google or Amazon you need to master DSA (data structures and algorithms).

Studying DSA and solving LeetCode problems will help you become a more analytical thinker and a better problem solver.

Learning DSA will teach you how to solve problems more efficiently.

Mistake #3 – Not having a good understanding of systems design

System design is about designing a software or hardware system to meet specific requirements.

Here are some important system design concepts to understand:

-Scalability: The ability of a system to handle increasing load (users, data, requests) without compromising performance.

-KISS (Keep It Simple, Stupid): Emphasizing simplicity in design to avoid unnecessary complexity.

-Databases (SQL vs. NoSQL): Understanding the trade-offs and use cases for relational databases (SQL) and various NoSQL databases (Key-Value, Document, Column-Family, Graph).

-Caching: Storing frequently accessed data in a temporary storage area (cache) to reduce latency and improve read performance.

-Load Balancing: Distributing incoming network traffic across multiple servers to ensure no single server bears too much load, enhancing system reliability and performance.

-Content Delivery Network (CDN): A network of geographically distributed servers that deliver web content to users based on their location, improving load times.

-CAP Theorem: States that a distributed system can only provide two out of three guarantees: Consistency, Availability, and Partition tolerance.

-Rate Limiting/Throttling: Controlling the rate at which users can access APIs or services to prevent abuse and ensure fair usage.

Mistake #4 – Not being a good communicator. Bad communication skills will have a negative impact on your career and will make it difficult to work with other programmers on your team.

Here are a few examples of good communication skills:

-Writing clear and concise code
-Thoughtful commit messages
-Explaining technical concepts to non-technical stakeholders
-Explaining your thought process when solving a coding interview problem
-Writing documentation that is well structured and easy to understand

Mistake #5 – Trying to learn too many tech stacks. This is a mistake a lot of newbies make because they believe learning a bunch of tech stacks at one time is the best way to showcase their skills & stay current.

This is what you don’t want to do. Pick one popular tech stack (MERN for example) and master it.

You dont want to be a jack of all trades and a master of none!

Conclusion – Avoiding these mistakes will be the key to your long term success as a software engineer.

What are some common mistakes you see software engineers make?

Let us know in the comments section below!

Check out some awesome offers below:

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Let me and my team build you a money making website or blog…https://bit.ly/tnrwebsite_service

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

 

{ 0 comments }