5

我试图在进行 mongodb 查询时避免使用回调。我正在使用 mongoskin 拨打电话,如下所示:

req.db.collection('users').find().toArray(function (err, doc) {
  res.json(doc);
});

在许多情况下,我需要进行多个查询,因此我想使用 Node.js 的 promise 库,但我不确定如何将这些函数包装为 promise。我看到的大多数示例对于诸如此类的事情都是微不足道的readFile,我猜在这种情况下我需要以某种方式包装 toArray?这可以做到还是必须由 mongoskin 实现?

一个例子可以是任何一组回调,find/insert,find/find/insert,find/update:

req.db.collection('users').find().toArray(function (err, doc) {
  if (doc) {
    req.db.collection('users').find().toArray(function (err, doc) {
      // etc...
    });
  }
  else {
    // err
  }
});
4

3 回答 3

4

您可以使用 bluebird 像这样承诺整个模块:

var Promise = require("bluebird");
var mongoskin = require("mongoskin");
Object.keys(mongoskin).forEach(function(key) {
  var value = mongoskin[key];
  if (typeof value === "function") {
    Promise.promisifyAll(value);
    Promise.promisifyAll(value.prototype);
  }
});
Promise.promisifyAll(mongoskin);

这只需要在您的应用程序中的一个地方完成一次,而不是在您的应用程序代码中的任何地方。

之后,您只需正常使用方法,除了 Async 后缀并且不传递回调:

req.db.collection('users').find().toArrayAsync()
  .then(function(doc) {
    if (doc) {
      return req.db.collection('users').find().toArrayAsync();
    }
  })
  .then(function(doc) {
    if (doc) {
      return req.db.collection('users').find().toArrayAsync();
    }
  })
  .then(function(doc) {
    if (doc) {
      return req.db.collection('users').find().toArrayAsync();
    }
  });

再说一次,如果你调用一个函数

foo(a, b, c, function(err, result) {
    if (err) return console.log(err);
    //Code
});

承诺返回版本的调用如下:

fooAsync(a, b, c).then(...)

(未捕获的错误会自动记录,因此如果您只是要记录它,则无需检查它们)

于 2014-05-15T20:25:41.083 回答
2

只是在这里偶然发现了同样的问题并且不喜欢“有前途的” mongoskin ,所以做了更多的挖掘并找到了和尚。它建立在 mongoskin 之上,整理 API 并为所有异步调用返回 Promise。可能值得一看其他登陆这里的人。

于 2014-09-15T19:50:48.683 回答
1

Esailija 的答案可能有效,但效率不高,因为您必须在每个 db 调用上运行 db.collection。我不知道这到底有多贵,但看看 mongoskin 中的代码,它的重要性不言而喻。不仅如此,它还在全局修改原型,这不是很安全。

我使用纤维期货的方法是:

  1. 包装每个集合的集合方法
  2. 在收到结果时,对于返回 Cursor 的方法,包装 toArray 方法,调用它并返回结果未来(对于不返回游标的方法,您不需要做任何其他事情)。
  3. 正常使用未来

像这样:

var Future = require("fibers/future")

// note: when i originally wrote this answer fibers/futures didn't have a good/intuitive wrapping function; but as of 2014-08-18, it does have one
function futureWrap() {
    // function
    if(arguments.length === 1) {
        var fn = arguments[0]
        var object = undefined

    // object, methodName
    } else {
        var object = arguments[0]
        var fn = object[arguments[1]]
    }

    return function() {
        var args = Array.prototype.slice.call(arguments)
        var future = new Future
        args.push(future.resolver())
        var me = this
        if(object) me = object
        fn.apply(me, args)
        return future
    }
}

var methodsYouWantToHave = ['findOne', 'find', 'update', 'insert', 'remove', 'findAndModify']
var methods = {}
methodsYouWantToHave.forEach(function(method) {
    internalMethods[method] = futureWrap(this.collection, method)
}.bind(this))

// use them
var document = methods.findOne({_id: 'a3jf938fj98j'}, {}).wait()
var documents = futureWrap(methods.find({x: 'whatever'}, {}).wait(), 'toArray')().wait()

如果您不想使用光纤,我建议使用async-future 模块,它也内置了很好的 wrap 功能。

于 2014-08-14T18:52:50.400 回答