Node.js promises with mongoskin

走远了吗. 提交于 2019-12-05 10:59:28

You can promisify the entire module like so with 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);

This only needs to be done in one place for one time in your application, not anywhere in your application code.

After that you just use methods normally except with the Async suffix and don't pass callbacks:

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

So again, if you call a function like

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

The promise-returning version is called like:

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

(Uncaught errors are automatically logged so you don't need to check for them if you are only going to log it)

Just stumbled here with the same question and didn't love "promisfying" mongoskin so did a bit more digging and found monk. It's built on top of mongoskin, tidies up the API and returns promises for all async calls. Probably worth a peek to anyone else who lands here.

Esailija's answer may work, but its not super efficient since you have to run db.collection on every single db call. I don't know exactly how expensive that is, but looking at the code in mongoskin, its non-trivial. Not only that, but it's globally modifying prototypes, which isn't very safe.

The way I do this with fibers futures is:

  1. wrap the collection methods for each collection
  2. on receiving the result, for methods that return a Cursor wrap the toArray method, call it and return the resulting future (for methods that don't return a cursor, you don't need to do anything else).
  3. use the future as normal

like this:

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()

If you don't want to use fibers, I'd recommend using the async-future module, which has a good wrap function built in too.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!