Tag: Loops
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.

Which JavaScript for loop should I use?

Which JavaScript for loop should I use?

When given a choice of 4 seemingly different ways to do the same thing, I've always been the person that wants to know what the difference is? To often we can blindly write code the way the person before us did and not understand the reason to do it a certain way, but what if the scenario has changed. When multiple options exist there's usually a reason why. It can be a language style improvement to make things easier to write, but more often than not there's a fundamental difference in what it actually does.

So, for loops, four seems like a lot, gotta be some differences right?

for

This is likely to be the first type of for loop you encounter when learning to program. It's a very manual type of loop where you create a initial expression, a condition expression to keep running the loop and an increment expression.

1for (let i = 0; i < 5; i++) {
2 console.log(i);
3}
4
5// Result:
6// 0
7// 1
8// 2
9// 3
10// 4

The big downside of the for loop is it doesn't actually loop through a collection. e.g. If you had an array and wanted to loop through each of it's objects you could do this as follows:

1const arr = ['a', 'b', 'c'];
2
3for (let i = 0; i < arr.length; ++i) {
4 console.log(arr[i]);
5}

In effect the loop is incrementing a number and you then use that number to access the position of the array. The result is you have more code to write that isn't serving any real purpose.

However as this is a much more manual loop process you have far more control over the loop. e.g.

  • You could increment by a different number.
  • You could go backwards.
  • The condition might not be linked to the length of an array.
  • You might not even be looping through an array.

Pros

  • Potentially faster in performance.
  • Break statement can be used to come out of the loop.
  • It works with the await keyword.
  • Not just for looping through arrays.

Cons

  • Not as readable as others.
  • You have more code to write.

for...in

You probably don't want this one. Here's an example:

1const arr = ['a', 'b', 'c'];
2
3for (const i in arr) {
4 console.log(arr[i]);
5}
6
7// Result:
8// "a"
9// "b"
10// "c"

Although we're now specifically looping through a collection like an array and don't need to do all of that i < array.length stuff, what we're given isn't the object in the array but a property name to access it on the object.

Here's another example:

1const arr = ['a', 'b', 'c'];
2arr.foo = 'John'
3
4for (const i in arr) {
5 console.log(arr[i]);
6}
7
8// Result:
9// "a"
10// "b"
11// "c"
12// "John"

That looks a bit weird! The for...in loop doesn't just loop through an array, it loops through an objects enumerable properties, which could be the items in an array, but it could be other stuff too.

For this reason, unless your doing something quite niche its probably not the loop you are looking for and is likely to cause you an issue which your objects has one more property than you were expecting.

Pros

  • Can loop through all the enumerable properties on an object.

Cons

  • Looping through all the properties of an object isn't likely what you want.

forEach

As the name implies, a for each loop will iterate through each element in an array. Here's an example:

1const arr = ['a', 'b', 'c'];
2
3arr.forEach((i) => {
4 console.log(i);
5})
6
7// Result:
8// "a"
9// "b"
10// "c"

The forEach function takes an anonymous function as a parameter and will then call that function for each object in the array, passing in the object from the array.

This offers a big improvement over the initial for loop as we have a lot less code to write and we're actually given the object rather than a variable to go an find it.

However there are some downsides due to it effectively being a function call passing an anonymous function to execute.

Firstly you can't stop the forEach part way through. With the others you can use the break keyword to stop the iteration.

Secondly there's added confusion around the scope of what this is. Assuming your function is in a class, unless you use the arrow syntax (as in the example) you won't be able to access any of the other functions in your class as passing a regular function would change the scope.

1// This works
2class foo
3{
4 myFunction() {
5 const arr = ['a', 'b', 'c'];
6
7 arr.forEach((i) => {
8 this.outputValue(i)
9 })
10 }
11
12 outputValue(x) {
13 console.log(x);
14 }
15}
16
17// This Doesn't
18class foo
19{
20 myFunction() {
21 const arr = ['a', 'b', 'c'];
22
23 arr.forEach(function(i) {
24 this.outputValue(i)
25 })
26 }
27
28 outputValue(x) {
29 console.log(x);
30 }
31}

You also can't use an await within the foreach loop. e.g.

1async function run() {
2 const arr = ['a', 'b', 'c'];
3 arr.forEach(el => {
4 // SyntaxError
5 await new Promise(resolve => setTimeout(resolve, 1000));
6 console.log(el);
7 });
8}

Pros

  • Much shorter to write and easier to read.
  • Iterator provides the object from the array.

Cons

  • Easy to mess up context of this.
  • No way to break the loop.
  • Async/Await doesn't work on functions within the loop.
  • Can only loop through arrays.
  • It's performance is slower (nice article here comparing the performance differences), not to such an extent it would matter on a small object, but large objects could start to take a hit.

for...of

When looping through an array, the for...of loop combines the best of all the other loops. It has the same simple syntax of the for...in loop but instead of enumerating over enumerable properties, it loops through the iterable objects such as the items in an array.

Like the forEach loop you are provided with the object in the array rather than it's index or property name.

1const arr = ['a', 'b', 'c'];
2
3for (const i of arr) {
4 console.log(i);
5}
6
7// Result:
8// "a"
9// "b"
10// "c"

You can also break the loop and access properties outside of the loop.

1const stopValue = 'b'
2const arr = ['a', 'b', 'c'];
3
4for (const i of arr) {
5 console.log(i);
6 if (i == stopValue)
7 break;
8}
9
10// Result:
11// "a"
12// "b"

Pros

  • Lowest amount of extra code to write.
  • Iterator provides the object.
  • Doesn't use an anonymous function so scope doesn't change.
  • Loop can be stopped as needed.
  • Async still works.
  • Works with more than just arrays.

Cons

  • Have to be looping over the iterable items in an object.
  • Can't easily access the index value.

Conclusion

If you want to iterate through something like an array, for...of would be my recommendation to use. A for...in loop is likely to be less relevant but has its place, and for all other loops which don't relate to the objects in an array a good old for loop still has it's place.