Parallel computation to be merged, depending on single call
Option 1 defeats the whole purpose of Promises. You are nesting the callbacks. Option 2 and 3 are not different, just how they're formatted. That's up to personal preferences.

Now going with the fact that get_phase_count, get_phase_info and get_phase_votes are wrapper functions to API calls, let's not touch those. That leaves us with join_phase_promises. join_phase_promises is unnecessary as it's just a wrapper for Promise.all which you can simply use directly.

Now over to code styles, JS follows camelCase convention. This makes your function names, which are underscore-cased, look like outcasts. Suggesting you use camelCase instead.

If you happen to use Node.js 4+ (current version is 5.7), the native Promise is already available. No need to use Bluebird for that. You can also start using arrow functions to make your callbacks a bit more compact. Lastly, JS has a bunch of array operations and recently included destructuring. There's little need to use underscore.

If I were to write your function, it would go like

getPhaseCount().then(amount =&gt; Promise.all([
  getPhaseInfo(amount),
  getPhaseVotes(amount)
])).then(args =&gt; {
  let merged = args.reduce((p,c) =&gt; c.map((a, i) =&gt; ({...a, ...p[i]}) ), []);
  console.log(merged);
});


The above logic took me a while to figure out using only native operations, but what it does is to allow us to see the previous and current values using reduce. This way, on each item, map essentially creates a new array containing a merged version of the previous and current.