Search code examples
javascriptnode.jsasynchronouspromisechaining

What patterns are there for passing state through a chain of promises in Javascript?


I'm trying to learn a little about Node and asynchronous programming. I read about Promises and have made an attempt at using them in a small project that copies posts for a user from Service A to Service B. I am having some trouble understanding how best to pass state between Promises

The project is written for NodeJS using the Promise library

A simple definition of my current problem is:

  • Copy posts for a user from Service A to Service B if the posts don't already exist in Service B.
  • Both services offer http APIs that requires a non-memorable user id to look up posts for that user so user id must be looked up from the user name.
  • All of the http calls are asynchronous.

This is some pseudo code that illustrates how I have chained the Promises together.

Promise.from('service_A_username')
  .then(getServiceAUserIdForUsername)
  .then(getServiceAPostsForUserId)
  .then(function(serviceAPosts) {
    // but what? store globally for access later?
    doSomethingWith(serviceAPosts);
    return Promise.from('service_B_username');
  })
  .then(getServiceBUserIdForUsername)
  .then(getServiceBPostsForUserId)
  .done(function(serviceBPosts) {
    // how do we interact with Service A posts?
    doSomethingThatInvolvesServiceAPostsWith(serviceBPosts); 
  });

There are a couple of things that I have thought about doing:

  1. Bring the getIdForUsername call inside the getPostsForUserId function. However, I wanted to keep each unit of functionality as simple as possible along the principle of 'do one thing and do it well'.
  2. Create a 'context' object and pass it through the whole chain, reading and storing state in this object. However this approach makes each function very bespoke to a chain and therefore difficult to use in isolation.

Are there any other options, and what approach is recommended?


Solution

  • I would use Promise.all, like this

    Promise.all([Promise.from('usernameA'), Promise.from('usernameB')])
        .then(function(result) {
            return Promise.all([getUsername(result[0]),getUsername(result[1])])
        })
        .then(function(result) {
            return Promise.all([getPosts(result[0]),getPosts(result[1])]);
        })
        .then(function(result) {
            var postsA = result[0], postsB = result[1];
            // Work with both the posts here
        });