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, его нетривиально. Не только это, но и глобальное изменение прототипов, что не очень безопасно.
Я делаю это с фьючерсами на волокна:
- обернуть методы коллекции для каждой коллекции
- при получении результата для методов, которые возвращают Cursor, оборачивают метод toArray, вызывают его и возвращают полученное будущее (для методов, которые не возвращают курсор, вам больше ничего не нужно делать).
- используйте будущее как обычно
как это:
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, в который также встроена хорошая функция переноса.