我有一个递归与承诺发生的情况,我需要一些帮助。
我有一个包含一系列消息的数组。每条消息可以是:1 个基元,如字符串;或 2 复合,它只是一个指向包含消息序列的数据库区域的指针。例如,数组可以是:['hello', (compound), 'there']
,其中(复合)可以包含从数据库获取复合消息时的['how', 'are', 'you']
。因此,最终的"扩展"数组如下所示:['hello', 'how', 'are', 'you', 'there']
。将消息数组完全转换为原始消息数组称为"扩展"。
请注意,复合消息中的消息也可以是复合消息,这就是递归的用武之地。例如,如果上面例子中的(化合物)是['how', 'are', 'you', (lower level compound)]
的,而(较低级别的复合)是['Tom', 'Jerry']
的,那么(化合物)将被扩展为['how', 'are', 'you', 'Tom', 'Jerry']
,而原始数组将被扩展为['hello', 'how', 'are', 'you', 'Tom', 'Jerry', 'there']
。
这是我认为代码的样子,没有承诺:
function expandMessages(messages, outputMessages) {
messages.forEach(function(message) {
if (message.primitive) {
outputMessages.push(message);
}
else {
var fetchedMessages = fetchMessages(message);
expandMessages(fetchedMessages, outputMessages);
}
});
}
在上面的代码中,fetchMessages
从数据库获取复合消息的消息。
我应该如何承诺上面的代码,以便在外部承诺返回后:1 outputMessages 只包含原始消息;并保持 2 正确的消息顺序,即任何复合消息的"子消息"都插入到复合消息在原始数组中的位置。
谢谢!
涉及承诺的唯一原因是当fetchMessages()
是异步的时,所以让我们假设是这样。
代码出奇地简单:
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);
}
其中flatten()
是:
function flatten(list) {
return list.reduce(function(a, b) {
return a.concat(Array.isArray(b) ? flatten(b) : b);
}, []);
};
演示
解释
[].concat(messages)
是一种安全措施,以防止messages
不是阵列,在这种情况下,messages.map()
会抛出。.map(...)
将字符串/数组/承诺的混合数组映射到字符串和承诺的混合数组。m.then(expandMessages)
和fetchMessages(m).then(expandMessages)
会导致递归发生。Q.all()
聚合字符串和承诺的混合数组,并提供字符串和数组的混合数组。.then(flatten)
将字符串和数组的混合数组减少为字符串数组。- 最终数组的顺序是您想要的,因为
Q.all(messages.map(...))
(在每个级别)提供一个与messages
一致的数组。 - 正如您将在演示中看到的那样,"复合消息"可以是 Array 或 promise 包装的 Array,这提供了您可能需要也可能不需要的一定程度的灵活性(但它是免费的)。