Nodejs Mongoose – 如何避免callback地狱?

在使用它一段时间后,我可能会对Mongoose做错了。 当你有太多的callback时,我发现很难阅读,例如:

我想find一个文件并更新它。 我需要先find它,然后update将在findOne的callback中。

 var sam = new Character({ name: 'Sam', inventory: {}}); Character.findOne({ name: 'Sam' }, function(err, character) { console.log(character); // now I want to update it. character.update({... }, function(err, characterID) { // now I want to know the size of the document after the update. Character.findOne({ _id: characterID }, function(err, character) { // Now update it again.... }); }); }); 

它最终结束了一个意大利面代码!

你明白我的意思吗?

任何想法如何做得更好?

或者,没有所有这些callback,mongodb本地更好?

编辑:

 Character.findOne({...}, function(err, character) { return character.update(...); }).select("-field1 -field2").then(function(data) { // }).catch(function(error) { // Handle any error from all above steps }).done(); 

错误:

 TypeError: Character.findOne(...).select(...).then(...).catch(...).done is not a function 

你可以在nodejs中使用q promise的链接

 var Q = require('q'); function findOne(filter) { return Q.Promise(function(resolve, reject) { Character.findOne(filter, function(err, character) { resolve({ err: err, character: character }); }); }); } function update(data) { return Q.Promise(function(resolve, reject) { character.update(data, function(err, characterID) { resolve({ err: err, characterID: characterID }); }); }); } findOne({ name: 'Sam' }).then(function(data) { if (!data.err) { // now you can update it. return update(data.character); } else { throw new Error(data.err); } }).then(function(data) { if (!data.err) { // now you can update it. return update(data.characterId); } else { throw new Error(data.err); } return findOne({ id: characterId }); }).then(function(data) { if (!data.err) { // now you can update it. return update(data.character); } else { throw new Error(data.err); } }).catch(function(error) { // Handle any error from all above steps }).done(); 

由于Mongoose支持承诺( 这里logging ),你可以重写你的代码:

 var sam = new Character({ name: 'Sam', inventory: {}}); Character.findOne({ name: 'Sam' }).then(character => { return character.update(...); }).then(characterID => { return Character.findOne({ _id: characterID }); }).then(character => { ... }).catch(err => { // TODO: handle error. }); 

不需要使用外部承诺库来包装每个使用的函数。 您可能会使用已弃用的承诺库获得有关Mongoose的警告,通过将其包含在代码的顶部可轻松解决这个问题。

 mongoose.Promise = global.Promise; 

logging在这里 。

但是,您可以用一个命令replace上面的整个承诺链:

 Character.findOneAndUpdate({ name : 'Sam' }, { $set : { inventory : {} } }, { new : true }).then(character => { ... }); 

logging在这里 。

你现在已经发现了“回拨地狱” 。 这不仅限于Mongoose或Mongo,而是Node.js编程的一部分,不会感到孤单。 我们所有人都必须处理它。

在MongoDB中(或者大多数数据库的任何数据事件),问题是发生的事件链,需要捕捉错误或使程序等待完成的步骤。

  1. Promises可以是callback的asynchronous或同步解决scheme。 您可以使用蓝鸟将您的事件包装在承诺中。 这需要了解.thenreturn结构。 然而实质上你所做的是:“做第一步,然后做第二步” (等等)。 请记住,依赖于编码, 您仍然需要捕获错误 ,并且您可以轻松地将整个代码转换为同步,这在Node.js中不是理想的情况。 但是,这将解决Mongo连接的“回拨地狱”问题。 我所说的是尽量不要把所有的代码都放到.then – > return范例中,而只是放在那些真正需要的地方。

  2. 新的MongoDB节点驱动程序 (v.2.2.5)比以前好多了。 如果你包含提供yield和return事件的co模块, Mongo已经实例化了ECMAScript 6 (关于asynchronous/等待,请看下面的内容,因为它本质上是一样的)。 这是驾驶员到现在为止的光年。 这将有助于您利用Mongo连接和CRUD操作摆脱“回拨地狱”。

所以摆脱“回拨地狱”正常的回应是使用承诺。

为了使事情更复杂一点,新的Node.js(不是LTS),但是尚未正式使用的7.7XX以上的版本使用ES7中已经期待已久的asynchronous/等待结构。 这应该让你的代码asynchronous承诺。 关于这个的外部文章可以在这里看到 。

根据您的应用程序和您的要求,尝试使用Promise,或者您可以简单地使用新的MongoDB驱动程序与Mongo的合作。 至于要使用哪个模块,无论是蓝鸟,co,还是别的什么,都取决于你。 当Node.js正式发布async / await并且它按照广告的方式工作时,现在是使用该模型而不是callback的好时机。