Como otimizar este código Node.js + q para evitar o inferno de retorno de chamada?

Estou usando o Q para evitar o retorno de chamada, mas cheguei a uma parte do meu código que não sei como organizar:

Estou procurando mensagens agendadas para serem entregues. Para cada um deles, tento enviá-los um por um e, se ele puder ser enviado, remove-o do database. A parte feia é ter um then () dentro de um loop for . Dessa forma acabo tendo promises aninhadas em vez de callbacks nesteds !!!! Sugestões?

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); } }); } }); 

A parte feia é ter um then () dentro de um loop for

Não, isso pode acontecer. Embora o estilo de functional programming das promises geralmente leve ao uso de .map() com um retorno de chamada de qualquer maneira 🙂 Looping é uma estrutura de controle e requer aninhamento (exceto pelo uso de exceções para stream de controle , isto é, ramificação). O que você não precisa fazer é aninhar promises em callbacks promissores, no entanto.

Eu simplificaria o seu corpo de loop para

 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); }); } 

Agora você pode fazer algo como

 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"); }); 

Sim, uma maneira de limpá-lo seria definir uma function nomeada:

 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); } } 

e depois:

 .then(SuccessfulSend); 

Simplesmente refatorar suas funções em funções nomeadas e tentar usar melhor o encadeamento de promises seria uma grande melhoria.

 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; }); }