Node.js
async.each
synchronous HTTP calls
JavaScript
asynchronous programming

how to make synchronous http calls within async.each in nodejs

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Node.js, the right answer is usually not to make synchronous HTTP calls at all. Network I O is inherently asynchronous in the platform, and trying to force it into a blocking model defeats the event loop. If you need one request at a time inside async.each, use a serial iterator such as async.eachSeries or rewrite the flow with async and await.

Why Synchronous HTTP Is the Wrong Goal

Blocking the event loop means the whole process stops handling other work while one request is in flight. That is almost always a bad tradeoff for a server or worker process.

So there are two distinct goals that developers often confuse:

  • synchronous API, meaning the code looks sequential
  • synchronous execution, meaning the process is truly blocked

Node supports the first through callbacks, promises, and async / await. It should usually avoid the second.

Use async.eachSeries for Sequential Requests

If you are already using the async library and want each HTTP request to complete before the next begins, switch from each to eachSeries.

javascript
1const asyncLib = require('async');
2const https = require('https');
3
4function fetchUrl(url, done) {
5  https.get(url, res => {
6    let body = '';
7    res.on('data', chunk => body += chunk);
8    res.on('end', () => done(null, body));
9  }).on('error', done);
10}
11
12const urls = [
13  'https://example.com',
14  'https://example.org'
15];
16
17asyncLib.eachSeries(urls, (url, callback) => {
18  fetchUrl(url, (err, body) => {
19    if (err) return callback(err);
20    console.log(url, body.length);
21    callback();
22  });
23}, err => {
24  if (err) {
25    console.error('failed', err);
26    return;
27  }
28  console.log('done');
29});

This preserves sequential behavior without turning HTTP itself into a blocking operation.

Prefer async and await in Modern Code

For modern Node.js code, a plain loop with await is usually easier to read than callback-driven async.each helpers.

javascript
1async function fetchJson(url) {
2  const response = await fetch(url);
3  if (!response.ok) {
4    throw new Error(`HTTP ${response.status}`);
5  }
6  return response.json();
7}
8
9async function main() {
10  const urls = [
11    'https://jsonplaceholder.typicode.com/todos/1',
12    'https://jsonplaceholder.typicode.com/todos/2'
13  ];
14
15  for (const url of urls) {
16    const data = await fetchJson(url);
17    console.log(url, data.title);
18  }
19}
20
21main().catch(console.error);

This code is sequential in structure, but still asynchronous in execution. That is exactly what most Node applications want.

Limit Concurrency Instead of Forcing Sync

Sometimes the real need is not "fully synchronous", but "do not overwhelm the remote API". In that case, limiting concurrency is better than making requests strictly one by one.

Use async.eachLimit, a queue, or a small promise pool so the process stays efficient while respecting rate limits.

That usually gives better throughput and still protects external systems.

Respect Remote Service Limits

If these requests go to a third-party API, sequential execution may not be the only safe option. Often the better design is a small concurrency limit plus retry and backoff logic so your process stays efficient without violating rate limits. That usually solves the original concern better than forcing a blocking request model.

Common Pitfalls

  • Trying to block the Node event loop for network requests.
  • Using async.each when the workflow actually requires serial ordering.
  • Mixing callbacks, promises, and await in the same control path without a clear reason.
  • Solving rate-limit problems by forcing full serialization instead of using bounded concurrency.
  • Treating code that looks sequential as if it must also block the process.

Summary

  • In Node.js, truly synchronous HTTP calls are usually the wrong goal.
  • If you need sequential behavior with the async library, use eachSeries instead of each.
  • In modern code, async and await with a normal loop is often the clearest solution.
  • For throughput and external API safety, bounded concurrency is often better than strict serialization.
  • Keep the event loop non-blocking unless you have an unusually strong reason not to.

Course illustration
Course illustration

All Rights Reserved.