Nodejs Mongoose - 如何避免回调地狱?

时间:2017-03-25 09:31:55

标签: node.js mongodb mongoose callback

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

我想找一份文件并进行更新。我需要先找到它,然后update将在findOne的回调中。

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

});

最终会以意大利面条代码结束!

你明白我的意思吗?

任何想法如何做得更好?

如果没有所有这些回调,mongodb native更好?

修改

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

3 个答案:

答案 0 :(得分:2)

由于Mongoose支持promises(记录为here),您可以将代码重写为:

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

不需要使用外部promise库包装您使用的每个函数。您可能使用已弃用的承诺库获取有关Mongoose的警告,可以通过将其包含在代码顶部轻松修复:

mongoose.Promise = global.Promise;

记录here

但是,您可以使用一个命令替换上面的整个promise链:

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

记录here

答案 1 :(得分:1)

您现在已经发现“callback hell”。这不仅限于Mongoose或Mongo,而是所有Node.js编程的一部分,并且不会感到孤独。我们所有人都必须处理它。

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

  1. Promise可以是回调的异步或同步解决方案。您可以使用bluebird将您的事件包装在Promises中。这需要了解.thenreturn结构。但实际上你所做的就是说:“做第1步,然后做第2步。” (等等)。但请记住,根据编码,您仍然需要捕获错误,并且您可以轻松地将整个代码转换为同步,这在Node.js中并不理想。但是,它将解决Mongo连接的“回调地狱”。我所说的是尽量不要养成将所有代码放入.then - >的习惯。返回范式,但只是那些真正需要它的地方。

  2. new MongoDB Node Driver (v.2.2.5)比以前好多了。 Mongo has instantiated ECMAScript 6如果你包含co模块,它提供了yield和return事件(请参阅下面的async / await,因为它基本上是一样的)。这是驱动程序到现在为止的光年。这将帮助您摆脱Mongo连接和CRUD操作的“回调地狱”。

  3. 因此,要摆脱“回调地狱”,正常的反应就是使用承诺。

    为了使问题更复杂一点,新的Node.js(不是LTS),但7.7X以上的版本尚未正式使用期待已久的 async / await ES7的结构。这应该使您的代码与promises保持同步。一个outside article on this can be seen here

    根据您的应用和您的要求,尝试使用Promises,或者您只需将新的MongoDB驱动程序与co一起用于Mongo。至于使用哪个模块,无论是bluebird,co还是其他什么,都取决于你。当Node.js正式发布async / await并且它按照宣传的方式工作时,现在是使用该模型而不是回调的好时机。

答案 2 :(得分:1)

您可以使用q promise in nodejs

的链接
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();