Reputation: 1664
I have a scenario where recursion occurs with promises, and I need some help on this.
I have an array that contains a sequence of messages. Each message can be either: 1 primitive, like a string; or 2 compound, which is just a pointer to a DB area that contains a sequence of messages. For example, the array can be: ['hello', (compound), 'there']
, where (compound) can contain ['how', 'are', 'you']
when the compound message is fetched from DB. Therefore, the final 'expanded' array looks like: ['hello', 'how', 'are', 'you', 'there']
. To completely convert an array of messages into an array of primitive messages is called 'expand'.
Note that a message in a compound message can be also a compound message, this is where recursion gets in. For instance, if (compound) in the above example is ['how', 'are', 'you', (lower level compound)]
, and (lower level compound) is ['Tom', 'Jerry']
, then (compound) will be expanded into ['how', 'are', 'you', 'Tom', 'Jerry']
, and the original array will be expanded into ['hello', 'how', 'are', 'you', 'Tom', 'Jerry', 'there']
.
Here is what I think the code looks like, without promises:
function expandMessages(messages, outputMessages) {
messages.forEach(function(message) {
if (message.primitive) {
outputMessages.push(message);
}
else {
var fetchedMessages = fetchMessages(message);
expandMessages(fetchedMessages, outputMessages);
}
});
}
In the above code, fetchMessages
fetches messages from DB for a compound message.
How should I promisify the above code, so that after the outer promise returns: 1 outputMessages contains only primitive messages; and 2 correct message order is maintained, that is, any compound message's 'submessages' are inserted to where the compound messages were in the original array.
Thanks!
Upvotes: 0
Views: 66
Reputation: 19298
The only reason to involve promises is when fetchMessages()
is asynchronous, so let's assume that to be so.
The code is surprisingly simple :
function expandMessages(messages) {
return Q.all([].concat(messages).map(function(m) {
return Array.isArray(m) ? fetchMessages(m).then(expandMessages) : m.then ? m.then(expandMessages) : m;
})).then(flatten);
}
where flatten()
is :
function flatten(list) {
return list.reduce(function(a, b) {
return a.concat(Array.isArray(b) ? flatten(b) : b);
}, []);
};
Explanation
[].concat(messages)
is a safety measure to guard against messages
not being Array, in which case messages.map()
would throw..map(...)
maps a mixed array of Strings/Arrays/Promises to a mixed Array of Strings and Promises.m.then(expandMessages)
and fetchMessages(m).then(expandMessages)
cause recursion to happen.Q.all()
aggregates the mixed array of Strings and Promises, and delivers a mixed array of Strings and Arrays..then(flatten)
reduces the mixed array of Strings and Arrays to an array of Strings.Q.all(messages.map(...))
delivers (at each level) an array which is congruous with messages
.Upvotes: 1