Promise.object?

I love Promise.all(), but there's one big thing that bugs me about it: labeling.

Here's how Promise.all works - you put in an iterable (like an array). Any promises in that array are resolved; when and only when all the promises have been resolved, the array gets resolved, and you can grab it through .then();

For example:

const asyncDouble = (x) => new Promise((resolve, reject) => {  
  setTimeout(() => {
    resolve(x * 2); 
  }, 100)
}

const asyncTriple = (x) => new Promise((resolve, reject) => {  
  setTimeout(() => {
    resolve(x * 3); 
  }, 200)
}

const syncSquare = (x) => x * x; 

Promise.all([  
  asyncDouble(2),
  asyncTriple(3),
  syncSquare(4),
]).then((values) => {
  console.log(values) //>> [4, 9, 16]
})

Not only does this allow you to run multiple promises at once, because it also works with synchronous functions, it allows you to do clever things like returning a value from a cache if it exists; or if it doesn't, making an async request to get it, without worrying about having to wrap the answer in "Promise.resolve("foo") if you're getting it from the cache.

More importantly, because it takes an array as it's parameter, you can use the .map() method to great effect.

let arr = ['http://someapi.com', 'http://someotherapi.com', 'http://athirdapi.com']

Promise.all(arr.map((url) => new Promise((resolve, reject) => {  
  request(url, (err, response, body) => {
    resolve(body); 
  })
})).then((webpages) => {
  console.log(webpages) 
  //>> [<content of someapi>, <content of someotherapi>, <content of athirdapi>]
})

But one problem that I have is that Promise.all only works with arrays. If I wanted to do the above, but label each item into an array, I'd have to do something like this.

let arr = ['http://someapi.com', 'http://someotherapi.com', 'http://athirdapi.com']

Promise.all(arr.map((url) => new Promise((resolve, reject) => {  
  request(url, (err, response, body) => {
    resolve(body); 
  })
})).then((webpages) => {
  return {
    someApi: webpages[0],
    someOtherApi: webpages[1],
    aThirdApi: webpages[2], 
  }
}).then((webObj) => {
  console.log(webObj); //>>
/* 
{
   someApi: <content>,
   someOtherApi: <content>,
   aThirdApi: <content>  
}
*/
})

This is doable, but it has two problems. First, it requires you to reference the array's objects by literal numbers, which can be a major pain if the structure of the code changes. Secondly, this only really works well for "flat" objects - that is, objects that don't have nested objects.

I'm wondering if there's a way to shim "Promise.all" into a Promise.wholeObject() method... well, I mean, there are several ways, but I'm trying to think of the best way. Queue/Stack? Recursion? I'll come back to this topic later, I'm sure.

Brian Boyko

Read more posts by this author.

Austin, Texas

Subscribe to Brian Boyko

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!