【问题标题】:How to optimize this Node.js + q code to prevent callback hell?如何优化这段 Node.js + q 代码以防止回调地狱?
【发布时间】:2014-12-10 13:26:39
【问题描述】:

我正在使用 Q 来防止回调地狱,但我已经到达了我不知道如何安排的部分代码:

我正在搜索要发送的预定消息。对于它们中的每一个,我尝试将它们一个一个发送,如果可以发送,则将其从数据库中删除。丑陋的部分是在 for 循环中有一个 then()。这样我最终得到了嵌套的承诺而不是嵌套的回调!!!!有什么建议吗?

appLog.debug("Looking for scheduled messages");
var messages = messageService.findScheduled()
.then(function(messages){
    appLog.debug("found [%d] stored messages",messages.length);
    for(var i = 0; i<messages.length; i++){
        messageService.send(msg.namespace, msg.message, msg.data)
        .then(function(result) {
            if (result == constants.EVENT_EMIT_SENT) {
                appLog.debug("Message [%s] sent!!!", msg._id);
                messageService.remove(msg._id)
                    .then(function(result) {
                         appLog.debug("Message deleted: [%s]", msg._id);
                    })
                    .fail(function(err) {
                        appLog.error("The message couldn't be deleted: [%s]", msg._id);
                    });

            }else if (result == constants.EVENT_EMIT_NOT_SENT_AND_NOT_STORED) {
                appLog.debug("Message [%s] not sent", msg._id);
            }

        });
    }
});

【问题讨论】:

  • 一种方法是不使用q,而是使用bluebirdq 很慢。
  • 我是不是错过了let msg = messages[i]
  • @majidarif:不过,使用 bluebird 并不能解决您的代码布局问题。

标签: node.js callback promise q


【解决方案1】:

丑陋的部分是在 for 循环中有一个 then()

不,这可能会发生。尽管 promise 的函数式编程风格通常会导致使用 .map() 和回调 :-) 循环是一种控制结构,并且 需要 嵌套(除了你 use exceptions for control flow,即分支)。不过,您不必做的是在 Promise 回调中嵌套 Promise。

我会将你的循环体简化为

function sendAndDelete(msg) {
    return messageService.send(msg.namespace, msg.message, msg.data)
    .then(function(result) {
        if (result == constants.EVENT_EMIT_SENT) {
            appLog.debug("Message [%s] sent!!!", msg._id);
            return msg._id;
        } else if (result == constants.EVENT_EMIT_NOT_SENT_AND_NOT_STORED) {
            appLog.debug("Message [%s] not sent", msg._id);
            throw new Error("Message not sent");
        }
    })
    .then(messageService.remove)
    .then(function(result) {
         appLog.debug("Message deleted: [%s]", msg._id);
    }, function(err) {
         appLog.error("The message has not been deleted: [%s]", msg._id);
    });
}

现在你可以做类似的事情

messageService.findScheduled()
.then(function(messages){
    appLog.debug("found [%d] stored messages",messages.length);
    return Q.all(messages.map(sendAndDelete));
})
.then(function() {
    appLog.debug("all messages done");
});

【讨论】:

  • 嗨,Bergi!感谢您的解决方案。我正在测试它,但使用这一行 -> return Q.all(messages.map(sendAndDelete));永远不会调用 sendAndDelete 方法。如果我删除 Q.all,留下:return messages.map(sendAndDelete),尽管我在“消息 [%s] 已发送!!!”之前的日志中看到“所有消息已完成”,但它仍然有效.你知道为什么吗?它应该是最后一条消息。为什么 Q.all 不起作用?
  • messages.map(sendAndDelete) 调用它但Q.all(messages.map(sendAndDelete)); 没有调用它是没有意义的,除非还有其他类似语法错误的东西。如果你省略它(并且不从then 回调返回一个承诺),那么它不知道有什么要等待的,只是用undefined 解决下一个承诺,立即调用“完成”消息.注意我已经更新了sendAndDelete,当然它也需要返回一个promise。
【解决方案2】:

只需将您的函数重构为命名函数并尝试更好地利用承诺链将是一个巨大的改进。

var messageService, appLog, constants;

appLog.debug("Looking for scheduled messages");

messageService.findScheduled()
  .then(function(messages){
    appLog.debug("found [%d] stored messages", messages.length);
    messages.map(processMessage);
  });

function processMessage(msg) {

  msg.data.dontStore = true;
  messageService

    .send(msg.namespace, msg.message, msg.data)

    .then(function(result) {

      if (result !== constants.EVENT_EMIT_SENT) {
        throw new Error(constants.EVENT_EMIT_SENT);
      }

      appLog.debug("Message [%s] sent!!!", msg._id);
      return removeMessage(msg);

    })

    .fail(function(err) {
      appLog.debug("Message [%s] not sent", msg._id);
      throw err;
    });

}

function removeMessage(msg) {
  return messageService.remove(msg._id)
    .then(function() {
      appLog.message("Message deleted: [%s]", msg._id);
    })
    .fail(function(err) {
      appLog.message("The message couldn't be deleted: [%s]", msg._id);
      throw err;
    });
}

【讨论】:

    【解决方案3】:

    是的,清理它的一种方法是定义一个命名函数:

    function SuccessfulSend(result) {
            if (result == constants.EVENT_EMIT_SENT) {
                appLog.debug("Message [%s] sent!!!", msg._id);
                messageService.remove(msg._id)
                    .then(function(result) {
                         appLog.debug("Message deleted: [%s]", msg._id);
                    })
                    .fail(function(err) {
                        appLog.error("The message couldn't be deleted: [%s]", msg._id);
                    });
    
            }else if (result == constants.EVENT_EMIT_NOT_SENT_AND_NOT_STORED) {
                appLog.debug("Message [%s] not sent", msg._id);
            }
    
        }
    

    然后:

    .then(SuccessfulSend);
    

    【讨论】:

      猜你喜欢
      • 2019-03-16
      • 2019-09-18
      • 2023-03-22
      • 2017-04-06
      • 1970-01-01
      • 1970-01-01
      • 2013-02-15
      • 2016-01-01
      • 1970-01-01
      相关资源
      最近更新 更多