# Pyramidal promises

I have a situation in my Node (express) controller where I need to check whether a particular language has a parent language, if it does, I need to do a new DB (SQL) call to retrieve the parent language details.

The problem is that this is encouraging a pyramid style (not too far from callback-hell). What if next I need to do another DB call depending on something in the parent language? My code will rapidly become unreadable. I thought promises were meant to solve all this. Is there a better way?

Promise.all(promises)
.then(function(results) {
// check if the language has a macrolanguage, increases execution
// time of api, as queries cannot run in parallel
if (results[0].language.parent_language_id) {
Languages.getParentLanguageDetails(results[0].language.parent_language_id)
//embedded thens are bad style, should be cleaned up
.then(function(newResult) {
results[results.length] = newResult
var data = u.extendResultsObject(results)
res.send(200, data) //success
})
} else {
var data = u.extendResultsObject(results)
res.send(200, data) //success
}
})

• You can unnest it if you return a promise. See the equivalence dpaste.com/04DRXK7 – elclanrs Sep 3 '14 at 23:26
• @elclanrs You should make that an answer – Flambino Sep 3 '14 at 23:27
• I'd like to see some semi-colons in that code... Yes, it works without them, but that's relying on JS treating your code as malformed (since it strictly speaking is), and trying to fix it. JS has semi-colons for a reason; the auto-insertion thing is a wobbly crutch to lean on. – Flambino Sep 3 '14 at 23:31
• @elclanrs Not using push was a temporary brain-fart. Can you explain what you mean by cache the id? I'm not sure how to remove the else condition without the second .then not receiving results if the result doesn't have a parent_language_id – Joseph Sep 4 '14 at 0:19
• @jamal, bad edit. You've removed the code as it was before the suggestions, when it was it a bad way. – Joseph Sep 4 '14 at 0:48

To unnest promises you just have to return a promise, then you can chain then calls, for example:

p1.then(function(data1) {
p2(data1).then(function(data2) {
...
})
})


Can be written as:

p1.then(function(data1) {
return p2(data1)
}).then(function(data2) {
...
})


Your edit looks better, all I would do is remove the extra else, cache the id, and use push instead of results[results.length]:

Promise.all(promises)
.then(function(results) {
var id = results[0].language.parent_language_id
if (id) {
return Languages.getParentLanguageDetails(id)
.then(function(res) {
results.push(res)
return results
})
}
return results
}).then(function(results) {
var data = u.extendResultsObject(results)
res.send(200, data)
})


You can go a bit further, by realizing that:

results.push(res)
return result


could be re-written as:

return results.concat(res)


If res is an array then this might yield unexpected results though.