Q 承诺的递归

Recursion with Q promises

我有一个使用 promise 进行递归的场景,我需要一些帮助。

我有一个包含一系列消息的数组。每条消息可以是: 1 个原语,如字符串;或 2 复合,它只是指向包含一系列消息的 DB 区域的指针。例如,数组可以是:['hello', (compound), 'there'],其中(compound)在从数据库中获取复合消息时可以包含['how', 'are', 'you']。因此,最终的 'expanded' 数组看起来像:['hello', 'how', 'are', 'you', 'there']。将消息数组完全转换为原始消息数组称为'expand'。

请注意,复合消息中的消息也可以是复合消息,这就是递归的用武之地。例如,如果上例中的(复合)是['how', 'are', 'you', (lower level compound)],并且(较低级别的复合)是['Tom', 'Jerry'],那么(compound)会展开成['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 从数据库中获取复合消息的消息。

我应该如何承诺上面的代码,以便在外部承诺之后 returns:1 outputMessages 仅包含原始消息;并且维护了 2 个正确的消息顺序,也就是说,任何复合消息的 'submessages' 都被插入到复合消息在原始数组中的位置。

谢谢!

涉及 promises 的唯一原因是 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);
    }, []);
};

DEMO

说明

  • [].concat(messages) 是防止 messages 不是数组的安全措施,在这种情况下 messages.map() 会抛出。
  • .map(...) 将 Strings/Arrays/Promises 的混合数组映射到字符串和 Promises 的混合数组。
  • m.then(expandMessages)fetchMessages(m).then(expandMessages) 导致递归发生。
  • Q.all()聚合字符串和Promises的混合数组,传递字符串和数组的混合数组。
  • .then(flatten) 将字符串和数组的混合数组简化为字符串数组。
  • 最终数组的顺序是您想要的,因为 Q.all(messages.map(...)) 提供(在每个级别)与 messages.
  • 一致的数组
  • 正如您将在演示中看到的,"compound message" 可以是 Array 或 promise-wrapped Array,它提供了一定程度的灵活性,您可能需要也可能不需要(但它是免费的)。