async programming
concurrency
non-blocking
parallel execution
JavaScript

Run two async functions without blocking each other

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Running two or more asynchronous functions concurrently without blocking each other is a fundamental concept in modern programming, especially in environments where I/O operations can be slow and affect overall performance. In this article, we'll delve into how you can achieve this using different asynchronous programming models. We'll explore asynchronous patterns in JavaScript (using async/await and Promise.all), Python (using asyncio), and some common best practices.

Concept of Asynchronous Programming

Asynchronous programming allows a program to initiate a potentially time-consuming task and then move on to other tasks without waiting for the first task to complete. This non-blocking approach is particularly useful when dealing with I/O-bound operations, such as network requests, file I/O, or database queries.

JavaScript Example

In JavaScript, the async and await keywords, along with Promise.all(), offer an elegant way to write asynchronous code.

Here is how you can run two async functions concurrently:

javascript
1async function fetchData(url) {
2  let response = await fetch(url);
3  return response.json();
4}
5
6async function processConcurrentTasks() {
7  const [data1, data2] = await Promise.all([
8    fetchData('https://api.example.com/data1'),
9    fetchData('https://api.example.com/data2')
10  ]);
11
12  console.log(data1, data2);
13}
14
15processConcurrentTasks();

In this example, fetchData is an async function that fetches data from a URL. Promise.all() is used to execute both fetch operations concurrently. The execution of data1 and data2 are independent, and neither blocks the other.

Python Example

In Python, the asyncio module is used for writing concurrent code. Here is how you can achieve similar functionality:

python
1import asyncio
2import aiohttp
3
4async def fetch_data(session, url):
5    async with session.get(url) as response:
6        return await response.json()
7
8async def process_concurrent_tasks():
9    async with aiohttp.ClientSession() as session:
10        results = await asyncio.gather(
11            fetch_data(session, 'https://api.example.com/data1'),
12            fetch_data(session, 'https://api.example.com/data2')
13        )
14    print(results[0], results[1])
15
16asyncio.run(process_concurrent_tasks())

In this Python code, fetch_data fetches data asynchronously using aiohttp, and asyncio.gather() is used to run both fetch functions concurrently.

Key Points and Comparisons

Here's a table comparing key features of JavaScript's and Python's approach:

FeatureJavaScriptPython
Syntaxasync/await, Promise.allasyncio, await, aiohttp
Concurrency ControlPromise.all()asyncio.gather()
Execution ModelEvent loopEvent loop
Supported VersionsES2017+Python 3.7+
Network LibrariesBuilt-in fetch Third-party (Axios)aiohttp, requests (blocking)
Main Use-CasesWeb applications, Node.js applicationsWeb scraping, I/O-bound applications

Considerations and Best Practices

Error Handling

  • JavaScript: Use try-catch blocks within async functions to handle potential errors.
javascript
1  async function processData(url) {
2    try {
3      let data = await fetchData(url);
4      console.log(data);
5    } catch (error) {
6      console.error("Error fetching data:", error);
7    }
8  }
  • Python: Use exception handling to manage errors.
python
1  async def process_data(url):
2      try:
3          data = await fetch_data(session, url)
4          print(data)
5      except Exception as e:
6          print("Error fetching data:", e)

Optimizations

  1. Batch Requests: Group requests in batches to reduce concurrent requests and avoid potential rate limits from APIs.
  2. Timeouts and Retries: Implement timeouts and retry logic to handle network instability.
  3. Concurrency Limits: In both languages, respect the concurrency limits of your environment to prevent overwhelming resources.

Advanced Patterns

  • JavaScript: Utilize libraries like RxJS for reactive programming to handle more complex async scenarios.
  • Python: Use more advanced patterns with asyncio.Task to create explicit tasks, or incorporate asyncio.Queue for producer-consumer scenarios.

Asynchronous programming, while powerful, requires careful design to ensure efficiency and reliability. By leveraging tools and best practices specific to the language, you can maximize the performance of your applications, prevent bottlenecks, and handle multiple tasks in a smooth, non-blocking manner.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.