Node.js обещает с Mongoskin

Я пытаюсь избежать использования обратных вызовов при выполнении запросов mongodb. Я использую Mongoskin, чтобы звонить так:

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

Во многих случаях мне нужно сделать несколько запросов, поэтому я хочу использовать библиотеку обещаний Node.js, но я не уверен, как обернуть эти функции как обещания. Большинство примеров, которые я вижу, тривиальны для таких вещей, как readFileЯ думаю, в этом случае мне нужно как-то обернуть в Array? Может ли это быть сделано или должно быть что-то реализовано Mongoskin?

Примером может быть любой набор обратных вызовов, найти / вставить, найти / найти / вставить, найти / обновить:

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

3 ответа

Решение

Вы можете пообещать весь модуль, например, с помощью 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(...)

(Необнаруженные ошибки автоматически регистрируются, поэтому вам не нужно проверять их, если вы собираетесь их регистрировать)

Просто наткнулся здесь на тот же вопрос и не любил "промизировать" монгоскин, поэтому немного покопался и нашел монаха. Он построен на основе mongoskin, приводит в порядок API и возвращает обещания для всех асинхронных вызовов. Вероятно, стоит заглянуть к любому, кто приземлится здесь.

Ответ Esailija может работать, но он не очень эффективен, так как вы должны запускать db.collection при каждом вызове db. Я не знаю точно, насколько это дорого, но, глядя на код в 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, в который также встроена хорошая функция переноса.

Другие вопросы по тегам