Node.js promises with mongoskin

非 Y 不嫁゛ 提交于 2019-12-22 07:48:08

问题


I'm trying to avoid using callbacks when making mongodb queries. I'm using mongoskin to make calls like so:

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

In many cases I need to make multiple queries so I want to use Node.js promise library but I'm not sure how to wrap these functions as promises. Most of the examples I see are trivial for things like readFile, I'm guessing in this case I would need to wrap toArray somehow? Can this be done or would have to be something implemented by mongoskin?

An example could be any set of callbacks, 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
  }
});

回答1:


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)




回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/23687538/node-js-promises-with-mongoskin

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