Skip to content

Instantly share code, notes, and snippets.

@ericandrewlewis
Last active July 16, 2017 14:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericandrewlewis/d65143a260e41240e4d66ee722a2fe61 to your computer and use it in GitHub Desktop.
Save ericandrewlewis/d65143a260e41240e4d66ee722a2fe61 to your computer and use it in GitHub Desktop.
Say When: Recursing Promises In JavaScript

Say When: Recursing Promises In JavaScript

Imagine there's an API that outputs ten items in a single request. You want to get all the items from this API, and you're gonna use JavaScript and promises. The problem is, you don't know if there's 2 pages of data or 200.

You can't do this well with a common .then() chain, because you don't know how many requests you'll need:

makeRequest()
  .then(makeRequest)
  .then(makeRequest)
  .then(makeRequest)
// Hmm...should I just make 1000 requests and toss out 
// the hundreds of 404s that are requests beyond the bounds of the data?

Basically, you want to run a task over and over for an unclear number of times.

The boilerplate for doing this with promises is

const doSomethingABunchOfTimes = () => {
  const doSomethingOneTime = () => {
    return somethingThatReturnsAPromise()
      .then(() => {
        if (theWorkIsComplete) {
          return someFinalValue;
        }
        return doSomethingOneTime();
      });
  }
  return doSomethingOneTime();
}

doSomethingABunchOfTimes()
  .then((someFinalValue) => {
    // do something with the final value
  });

doSomethingOneTime() will run over and over again in series until a condition is met (theWorkIsComplete()).

Here's a real-world example, where we get all the posts from my personal weblog's REST API:

const requestAsync = require('request-promise');

const getAllPagedResultsFromSomeAPI = () => {
  const getASinglePageOfResultsFromSomeAPI = (page, allApiResults = []) => {
    const url = `https://ericandrewlewis.com/wp-json/wp/v2/posts?page=${page}`;
    return requestAsync(url)
      .then((rawResponse) => {
        const apiResults = JSON.parse(rawResponse);
        allApiResults = [...allApiResults, ...apiResults];
        return allApiResults;
      })
      .then((allApiResults) => {
        page++;
        return getASinglePageOfResultsFromSomeAPI(page, allApiResults);
      })
      .catch((error) => {
        // The API returns a 400 if we're past the bounds of the data.
        if (error.statusCode === 400) {
          return allApiResults;
        }
        throw error;
      })
  }
  return getASinglePageOfResultsFromSomeAPI(1, []);
}

getAllPagedResultsFromSomeAPI()
  .then((allApiResults) => {
    // do something productive with all my blog posts like cut them into those paper snowflake chains
  });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment