I can't write a JavaScript for loop, and it does not matter

Featured on Hashnode

Subscribe to my newsletter and never miss my upcoming articles

I've been using JavaScript daily for 7 years, and I'm not able to remember the syntax of a JavaScript for loop.

Despite this fact, I'm a rather successful freelance developer. Recently I even had the awesome opportunity to work for Facebook, as the Docusaurus lead maintainer, writing the code for the framework that powers the documentation sites of Babel, Prettier, Jest, ReactNative...

I'll explain why I'm not able to remember such syntax, and why it does not matter much.


My story

TLDR: I'm a functional programmer

I've really started programming at the beginning of my engineer degree, around 2004 (before that, I was only able to hack some scripts for Counter-Strike console or IRC).

Most of our school teaching was based on Java, but we also saw a bit of C, C++, OCaml.

The first loop syntax I learned probably looked like this one:

List<Integer> numbers = Lists.newArrayList(1, 2, 3);

for (int i = 0; i < numbers.length; i++) {
   System.out.println(numbers.get(i));
}

Before I came out of school, Java 6 brought some new, simpler syntax:

List<Integer> numbers = Lists.newArrayList(1, 2, 3);

for (Integer number : numbers) {
   System.out.println(number);
}

At my first job, the Google Guava lib brought some new verbose functional syntax to Java, and I was able to do weird things with it 😅.

List<Integer> numbers = Lists.newArrayList(1, 2, 3);

Lists.newArrayList(Collections2.transform(numbers, new Function<Integer,Void>() {
  @Override
  public Void apply(Integer number) {
    System.out.println(number);
    return null;
  }
}));

This Guava lib got me intrigued by functional programming, and lead me to become a Scala developer since 2012, and I was finally able to use functional programming concepts (loops, but not only) without the ugly Java/Guava syntax.

val numbers = List(1, 2, 3)
numbers.foreach(println)

In 2013, ReactJS came out, and this totally changed my career path. At this time, I didn't like JavaScript much and was only able to hack some inline JQuery things in server-rendered pages. But as a startup CTO, I saw my team struggle with architecture, BackboneJS and RequireJS, and thought I had to become better at frontend to lead them.

AngularJS looked like the safer choice at this time, but a Scala developer colleague really pushed for React, which looked fancy and risky. All things made sense with the visionary post of David Nolen (The Future of JavaScript MVC Frameworks), and we finally adopted React in January 2014, as it seemed we would be able to use our functional programming knowledge to the frontend app as well, and make the UI more predictable.

Fast forward, it wasn't easy to be a React early-adopter for our critical app. All companies were building their own state management solution, trying to figure things out, and so we did, based on the ideas of David Nolen to hold a single immutable state in an atom (I was able to get a hacky time-travel working before Redux).

Since then both the JavaScript language and the ReactJS ecosystem have progressed a lot, and it's very common to use functional programming principles nowadays.

Why I can't write a JavaScript for loop?

As a long-time functional programmer, I simply don't write for loops very often.

Like anything you don't use regularly, you end up forgetting the syntax.

Today, many of us use ES5+ syntax (or Lodash/Ramda...) and some functional constructs. Using map, forEach, filter are the most illustrated examples in the JS community.

const numbers = [1, 2, 3]
numbers.forEach(number => console.log(number));

But we can go much further than that once we are more experienced with functional programming, and almost never write any for loops anymore.

Don't get me wrong, it's not necessarily a goal to not write for loops anymore, and I'm not telling you that you should remove all for loops of your production codebase.

Very often there's an alternative syntax possible for your loops that might be more expressive and easier to understand. After a while, you end up seeing a for loop as an implementation detail of a more elegant functional abstraction.

This more expressive syntax is not only for loops, and you can as well see a functional abstraction being an implementation detail of another higher-level abstraction.

Let's consider we want to increment the age of 2 brothers.

const brothers = {
  id1: {name: "Sébastien", age: 34},
  id2: {name: "Antoine", age: 23}
};

I very often see the array.reduce() operator used when a more expressive alternative was possible.

function incrementBrothersAges() {
  return Object.entries(brothers)
    .reduce((acc,[id,brother]) => {
      acc[id] = {...brother, age: brother.age + 1};
      return acc;  
    },{})
}

You know what? I really struggled to write this code.

My first attempt was not working at all (TypeScript would have helped).

function incrementBrothersAges() {
  return Object.entries(brothers)
      // acc is the first arg 
      .reduce(([id,brother],  acc) => {
        acc[id] = {...brother, age: brother.age + 1};
        // we must return the acc here
      },{});
}

Yet, writing this kind of transform is idiomatic for me, using higher-level functional programming abstractions, such as mapValues (included in lodash).

function incrementBrothersAges() {
  return mapValues(
    brothers, 
    brother => ({...brother, age: brother.age + 1})
  );
}

And I think nobody would argue that this is harder to read and maintain right? If junior developers are not familiar with functional programming, they'll catch up fast and get used to it. This might even be harder to learn reduce.

Why it does not matter?

I don't write for loops (or reduce), but I know the concepts. I know that these loops exist in different syntaxes, that can be useful for different use cases, and how to make a choice with a good tradeoff (performance, readability...).

I'll illustrate this with a concrete example from my daily work that actually led me to write this article.

I had this async function that performs some long task for a given country.

async function runCountryTask(country) {

  // Simulate a long async task (1 to 5 seconds)
  const taskDuration = 1000 + Math.random() * 4000;
  await new Promise(resolve => setTimeout(resolve, taskDuration));

  console.log(`Task completed for ${country}`);
}

This task had to be run for many countries, but the tasks should be run sequentially, not in parallel.

As I know the concepts, and I knew that the following would not work, as Promise.all would run all tasks in parallel.

async function runAllCountryTasks() {
  const countries = ["FR", "EN", "US", "DE", "UK", "IT"];

  // runs in parallel
  await Promise.all(countries.map(runCountryTask))
}

I also knew that there were multiple possible solutions to solve this problem:

  • use a third-party dependency exposing the higher-level async primitive I need
  • use Promise.then() recursively
  • use async/await, using a for loop syntax to iterate over a fixed-size array

I didn't want to introduce a new third party dependency just for a tiny utility function.

I also knew that using Promise.then() recursively could be harder to read, write, and maintain. There are many ways to write such a recursion, one of them could be:

async function forEachAsyncSequential(array, asyncFn) {
  await array.reduce((acc, item) => {
    return acc.then(() => asyncFn(item))
  }, Promise.resolve());
}

So I opted for a basic for loop, as it seemed the right tradeoff.

As I'm totally unable to remember the syntax (in vs of, can I actually use const?), I had to actually google it, and it didn't take me long to be able to write the TypeScript code that will be shipped in production.

export async function forEachAsyncSequencial<T>(
  array: T[],
  asyncFn: (t: T) => Promise<void>,
): Promise<void> {
  for (const item of array) {
    await asyncFn(item);
  }
}
async function runAllCountryTasks() {
  const countries = ["FR", "EN", "US", "DE", "UK", "IT"];

  // runs in sequence
  await forEachAsyncSequencial(countries, runCountryTask);
}

Believe me or not, but I think It's the only for loop I actually wrote in JavaScript this year. And once it's written, I won't need to write it ever again (at least for this project), as it's now part of my functional programming abstractions, that I can reuse anywhere I need.

JsFiddle playground


Conclusion

It's not very important to remember every syntax details to be productive in your daily work, particularly when you don't use them often (on purpose), as you prefer to work with more expressive, higher-level abstractions.

I had to google many things to write this article:

  • Syntax for declaring a Java list
  • Syntax for iterating a Java list
  • Does System.out.println accept an Integer?
  • Syntax for Scala string interpolation
  • Is there a forEach in Guava (actually found my own StackOverflow question)
  • What are the possible syntaxes for iterating over a JavaScript array
  • Signature of array.reduce()

Not remembering all this does not matter much, as long as I know what to look for.

In the same way, I don't know much about many other JavaScript things:

  • prototypes: I think I never hard to use them directly in my entire life, and I'm fine
  • classes: used them temporarily when I really had to in React
  • JavaScript quirks: I know some of them, but simply avoid the others by using ESLint, ===, TypeScript... it's not worth knowing all of them
  • ...

The knowledge and concepts you learn are more easily transposable from one language to another. I was able to learn React and contribute to its ecosystem quickly, thanks to my functional programming background.

I would argue that knowing how to do a recursive algorithm is more important than knowing the syntax of a for loop of a particular language. You will likely write many recursive algorithms in your career: the concept of recursion is not going anywhere anytime soon. But it's way more likely that you switch from one language to another from time to time.

Hopefully, writing this post will help me remember the syntax for a while until I forget it again 🤪.


🙏 If you like this post, please like it, share it or comment it 🙏:

For more content like this, subscribe to my mailing list and follow me on Twitter.

Sandeep Panda's photo

Nice write up. :-) Please tag it with JavaScript tag for better reach.

Sébastien Lorber's photo

Thanks, forgot the tags :)

Philippe Bernard's photo

Thank you for this article! I'm pleased to see I'm not the only one to not remember stuff considered basic. My favorite: finding out I've already upvoted the SO answer I'm looking for :)

Sébastien Lorber's photo

Thanks 😉

Or finding that you actually answered the question you ask for 10 years ago 😅 as I've got 70k on Stackoverflow it actually happens 😄

Kuldeep Bhatt's photo

This is a brilliant read! Absolute quality.

Younis Shah's photo

Enhanced for loop was introduced in Java 5, not Java 6.

Sébastien Lorber's photo

Ah good point. I guess my school teachers were using Java4 :D

Júlio César Ködel's photo

This is one of the most stupid article I began to read (could not read until the end, too painfull).

There is a lot of uses for indexed array access (including, for instance, comparing or using two correlated arrays with only one loop, instead of a nested loop).

Using a function call requires stack manipulation, context switching, etc. Is slow as hell. Thousands of times slow (but don't trust me, do a test, as any scientific method requires).

You are just a bad programmer and hashnode just became a Medium competitor (in article quality).

Sébastien Lorber's photo

I can indeed write for loops with indices if perf matters, but in most cases it's premature optimisation.

Do you always prematurely optimize? Do you feel a for loop is always the best solution for lists of size 10?