Parallel API calls in JavaScript

Parallel API calls in JavaScript

Although JavaScript is single-threaded, it is relatively simple to avoid blocking the thread by writing code in an asynchronous style through the use of functions with an async keyword that returns a promise and only awaiting when you need it.

For example, the following code contains an API call in an async function.

1 private async CallAPI() {
2 const response = await axios.get('https://fooapi.com/Films')
3
4 console.log(response.data)
5 }

If we wanted to call two APIs then we could do the following.

1 private async CallAPI() {
2 const response = await axios.get('https://fooapi.com/Films')
3 const response2 = await axios.get('https://fooapi.com/Music')
4
5 console.log(response.data)
6 console.log(response2.data)
7 }
8

However because both API calls are being immediately awaited, this will result in the execution of the function stopping while it waits for the result of the first API call before making the second.

As we don't need that response to make the second API call, we can improve the performance of the code by moving the await keywords to the point we actually need the responses. Both API calls will now be made in parallel rather than the second waiting for the first to finish.

1private async CallAPI() {
2 const response = axios.get('https://fooapi.com/Films')
3 const response2 = axios.get('https://fooapi.com/Music')
4
5 console.log(await response.data)
6 console.log(await response2.data)
7}

Call an API for each item in an array in parallel

Those initial examples were quite easy to understand, but what if we have a scenario where you get a list of films showing at the cinema on a particular day and display the show times for each? You have two APIs at your disposal; The first returns all the films and the second returns the showtimes for a given film.

To get all the data you need you first need to call the API to get the films and then loop through them to call the second API and get the show times.

1interface Film {
2 id: string
3 name: string
4 showtimes: ShowTime[]
5}
6
7interface ShowTime {
8 date: Date
9}
10
11class FilmService {
12 public async GetShowtimes(): Promise<Film[]> {
13 const filmResponse = axios.get('https://fooapi.com/Films')
14
15 const films: Film[] = []
16 for (let e of (await filmResponse).data) {
17 console.log("Get Showtimes")
18 const showTimes = await axios.get(
19 `https://fooapi.com/Films/${e.id}/ShowTimes`
20 )
21 console.log("Got Showtimes")
22
23 films.push({
24 id: e.id,
25 name: e.name,
26 showtimes: showTimes.data,
27 })
28 }
29
30 return films
31 }
32}
33
34// Output
35// Get Showtimes
36// Got Showtimes
37// Get Showtimes
38// Got Showtimes
39// Get Showtimes
40// Got Showtimes

The problem with this code though is that its execution length will be the films API call response time + (showtime API response time * the number of films).

As each call to the second API have no dependencies on each other, it doesn't make any sense for them to have to be performed sequentially in the loop.

Instead, we can refactor for loop to be a map calling an async function which returns the new array and make use of Promise.all() to ensure they have all completed before returning the results.

1interface Film {
2 id: string
3 name: string
4}
5
6interface FilmWithShowtime extends Film {
7 showtimes: ShowTime[]
8}
9
10interface ShowTime {
11 date: Date
12}
13
14class FilmService {
15 public async GetShowtimes(): Promise<FilmWithShowtime[]> {
16 const filmResponse = await axios.get('https://fooapi.com/Films')
17
18 const films: FilmWithShowtime[] = await Promise.all(
19 filmResponse.data.map(async (e: Film) => {
20 return await this.getFilmWithShowtimes(e)
21 })
22 )
23
24 return films
25 }
26
27 private async getFilmWithShowtimes(film: Film): Promise<FilmWithShowtime> {
28 console.log("Get Showtimes")
29 const showTimes = await axios.get(
30 `https://fooapi.com/Films/${film.id}/ShowTimes`
31 )
32
33 console.log("Got Showtimes")
34
35 return {
36 id: film.id,
37 name: film.name,
38 showtimes: showTimes.data,
39 }
40 }
41}
42
43// Output
44// Get Showtimes
45// Get Showtimes
46// Get Showtimes
47// Got Showtimes
48// Got Showtimes
49// Got Showtimes
50

The API calls for show times will now app be made in parallel reducing the overall execution time of the code.