asynchronous programming
JSON processing
callback functions
multiple files
JavaScript

Trigger callback after getting multiple json files asynchronously

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

When fetching multiple JSON files in JavaScript, you often need to wait for all requests to complete before running a callback. The standard approach is Promise.all(), which takes an array of promises and resolves when every promise has resolved, giving you all results at once. Before Promises, developers used manual counters or libraries like async.js. Modern JavaScript also provides async/await with Promise.all() for clean, readable code. The key concept is coordinating multiple asynchronous operations and running a single callback only after all have finished.

Promise.all with fetch

javascript
1const urls = [
2    '/data/users.json',
3    '/data/products.json',
4    '/data/orders.json'
5];
6
7// Fetch all JSON files in parallel
8Promise.all(urls.map(url => fetch(url).then(res => res.json())))
9    .then(([users, products, orders]) => {
10        // This runs only after ALL files are loaded
11        console.log('Users:', users.length);
12        console.log('Products:', products.length);
13        console.log('Orders:', orders.length);
14        renderDashboard(users, products, orders);
15    })
16    .catch(error => {
17        console.error('Failed to load data:', error);
18    });

Promise.all() runs all fetch requests concurrently and waits for every one to complete. The results array matches the order of the input promises, regardless of which request finishes first.

async/await with Promise.all

javascript
1async function loadAllData() {
2    const urls = [
3        '/api/config.json',
4        '/api/translations.json',
5        '/api/features.json'
6    ];
7
8    try {
9        const responses = await Promise.all(urls.map(url => fetch(url)));
10
11        // Check all responses are OK
12        for (const res of responses) {
13            if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.url}`);
14        }
15
16        // Parse all JSON bodies in parallel
17        const [config, translations, features] = await Promise.all(
18            responses.map(res => res.json())
19        );
20
21        initializeApp(config, translations, features);
22    } catch (error) {
23        showError('Failed to load application data', error);
24    }
25}
26
27loadAllData();

Using async/await with Promise.all() keeps the parallel execution benefit while making the code read sequentially. The two-step pattern (fetch all, then parse all) allows you to check HTTP status codes before parsing.

Manual Counter Pattern (Pre-Promise)

javascript
1// Older approach — useful for understanding the concept
2function loadMultipleJSON(urls, callback) {
3    const results = {};
4    let remaining = urls.length;
5
6    urls.forEach(url => {
7        const xhr = new XMLHttpRequest();
8        xhr.open('GET', url);
9        xhr.onload = function() {
10            results[url] = JSON.parse(xhr.responseText);
11            remaining--;
12
13            if (remaining === 0) {
14                callback(null, results);
15            }
16        };
17        xhr.onerror = function() {
18            callback(new Error('Failed to load ' + url));
19        };
20        xhr.send();
21    });
22}
23
24loadMultipleJSON(['/a.json', '/b.json', '/c.json'], function(err, data) {
25    if (err) return console.error(err);
26    console.log('All loaded:', Object.keys(data));
27});

This pattern uses a counter that decrements on each successful load. The callback fires when the counter reaches zero. Promise.all() replaces this pattern entirely.

Promise.allSettled for Partial Failures

javascript
1const endpoints = [
2    '/api/required-data.json',
3    '/api/optional-cache.json',
4    '/api/optional-analytics.json'
5];
6
7const results = await Promise.allSettled(
8    endpoints.map(url => fetch(url).then(r => r.json()))
9);
10
11results.forEach((result, i) => {
12    if (result.status === 'fulfilled') {
13        console.log(`${endpoints[i]}: loaded`, result.value);
14    } else {
15        console.warn(`${endpoints[i]}: failed`, result.reason);
16    }
17});
18
19// Extract only successful results
20const data = results
21    .filter(r => r.status === 'fulfilled')
22    .map(r => r.value);

Promise.allSettled() waits for all promises to complete but does not reject on failure. Each result has a status of either "fulfilled" or "rejected". Use this when some requests are optional and you want the rest to succeed.

Loading JSON with jQuery

javascript
1// jQuery.when() accepts multiple Deferreds
2$.when(
3    $.getJSON('/data/users.json'),
4    $.getJSON('/data/products.json'),
5    $.getJSON('/data/settings.json')
6).done(function(usersResult, productsResult, settingsResult) {
7    // Each result is [data, statusText, jqXHR]
8    const users = usersResult[0];
9    const products = productsResult[0];
10    const settings = settingsResult[0];
11
12    renderPage(users, products, settings);
13}).fail(function(jqXHR, textStatus, error) {
14    console.error('Load failed:', textStatus);
15});

Dynamic Number of Files

javascript
1async function loadJSONFiles(fileList) {
2    // fileList could come from an API or user input
3    const fetches = fileList.map(async (file) => {
4        const response = await fetch(`/data/${file}`);
5        if (!response.ok) {
6            throw new Error(`Failed to load ${file}: ${response.status}`);
7        }
8        return { name: file, data: await response.json() };
9    });
10
11    const results = await Promise.all(fetches);
12
13    // Convert array to object keyed by filename
14    const dataMap = Object.fromEntries(
15        results.map(r => [r.name, r.data])
16    );
17
18    return dataMap;
19}
20
21// Usage
22const data = await loadJSONFiles(['config.json', 'users.json', 'themes.json']);
23console.log(data['config.json']);

Common Pitfalls

  • Using Promise.all when any request can fail: Promise.all rejects immediately when any single promise rejects, discarding all other results. If some requests are optional, use Promise.allSettled() to get results from the requests that did succeed.
  • Sequential awaiting instead of parallel: Writing const a = await fetch(url1); const b = await fetch(url2); runs requests one after another. Use Promise.all([fetch(url1), fetch(url2)]) to run them concurrently. Sequential fetching can be 2-5x slower.
  • Assuming fetch rejects on HTTP errors: fetch() only rejects on network failures, not on 4xx or 5xx responses. Always check response.ok or response.status before calling response.json(). A 404 response will attempt to parse the error page as JSON and fail.
  • Not handling JSON parse errors: If the server returns invalid JSON (e.g., an HTML error page), response.json() throws a SyntaxError. Wrap the parse in a try-catch or validate the Content-Type header first.
  • Forgetting that Promise.all result order matches input order: Results from Promise.all always match the order of the input array, not the order in which requests completed. This is reliable for destructuring: const [a, b, c] = await Promise.all([fetchA, fetchB, fetchC]).

Summary

  • Use Promise.all() to fetch multiple JSON files in parallel and wait for all to complete
  • Use async/await with Promise.all() for readable, sequential-looking parallel code
  • Use Promise.allSettled() when some requests are optional and partial failure is acceptable
  • Always check response.ok before parsing — fetch() does not reject on HTTP errors
  • Results from Promise.all() maintain the same order as the input promises

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.