You dont need to fear recursion

Photo by Julia Kadel on Unsplash

You dont need to fear recursion

Recursion has a bit of a bad reputation. Unfairly I would say. Lets see if I can convince you.

All though, If I'm being honest. The concept of recursion was an aqcuired taste for me. It took a class of functional programming, where at least half of the assignements involved recursion for it to "click". Today I can find recursion to be concise and elegant way of solving certain problems.

Today I came across such a problem. This is similar to what I came across. getAllItems is a function that should fetch 100 items at a time, until there are no more items to fetch.

const getAllItems = async () => {
  let maxItems = 100;
  let page = 1;
  let allItems = [];

  while (maxItems === 100) {
    const items = await getItemsPage(page, maxItems);
    if (items.length < 100) {
      maxItems = items.length;
    }
    allItems.push(items);
    page++;
  }

  return allItems;
};

While I've probably written a ton of code like this in my day, but there are a couple of things that bothers me today.

  1. We mutate state. page, maxItems and allItems are all being mutated in the function. State mutation for me adds cognitive load and leads to less predictable code.

  2. The intent of maxItems is not to control the while loop, but number of items we should fetch for each page. Here it's used for both.

  3. All though it's almost always subjective, I don't find the code very readable.

Apart from fixing a bug in the function, I decided to refactor the function using recursion. The updated solution looked something like this:

const getAllItems = async (allItems = [], page = 1) => {
  const items = await getItemsPage(page, 100);
  if (items.length < 100) {
    return [...allItems, ...items];
  }

  return getAllItems([...allItems, ...items], page + 1);
};

In my opinion, this addresses all concerns of the first implementation. No mutation of state and more concise code.

There are times when I'm reluctant to reach for recursion. For example when I know I have a very large dataset that I need to traverse. I don't want to run into stack overflow issues.

When the function is long and complex or when performance is really important, are also examples where I might reach for another approach.

Recursion is vastly different to the "normal" iterations that most are used to. A function that calls itself? Sounds like witchcraft to me! However I'm here to tell you that you should invest time in getting familiar with the technique. It can make your code more robust and more readable. Just remember that with great power, comes great responsibility...