2014-05-15 17 views
5

Sto cercando di evitare l'uso di callback quando si effettuano query mongodb. Sto usando mongoskin per effettuare chiamate in questo modo:Node.js promette con mongoskin

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

In molti casi ho bisogno di fare più query quindi voglio usare biblioteca promessa Node.js ma non sono sicuro di come avvolgere queste funzioni come promesse . La maggior parte degli esempi che vedo sono banali per cose come readFile, immagino che in questo caso avrò bisogno di avvolgere toArray in qualche modo? Questo può essere fatto o dovrebbe essere qualcosa implementato da Mongoskin?

Un esempio potrebbe essere qualsiasi insieme di callback, trovare/inserto, trovare/ritrovare/inserto, trovare/aggiornamento:

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

risposta

4

È possibile promisify l'intero modulo in questo modo con la 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); 

Questa operazione deve essere eseguita in un unico punto per volta nell'applicazione, non in qualsiasi punto del codice dell'applicazione.

Dopo di che basta usare i metodi normalmente se non con il suffisso Async e non passano callback:

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

Quindi, di nuovo, se si chiama una funzione come

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

Il la versione di promessa di ritorno è chiamata come:

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

(Gli errori non rilevati vengono registrati automaticamente, quindi non è necessario controllarli se si sta solo per registrarlo)

1

La risposta di Esailija potrebbe funzionare, ma non è molto efficiente poiché è necessario eseguire db.collection in ogni singola chiamata db. Non so esattamente quanto sia costoso, ma guardando il codice in mongoskin, non è banale. Non solo, ma sta modificando globalmente i prototipi, che non sono molto sicuri.

Il mio modo di fare questo con fibers futures è:

  1. avvolgere i metodi di raccolta per ogni collezione
  2. nel ricevere il risultato, per i metodi che restituiscono un cursore avvolgere il metodo toArray, chiamarlo e restituire il risultante futuro (per i metodi che non restituiscono un cursore, non è necessario fare altro).
  3. utilizzare il futuro come normale

come questo:

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

Se non si desidera utilizzare fibre, mi consiglia di utilizzare the async-future module, che ha una buona funzione di involucro costruito in troppo .

2

Appena inciampato qui con la stessa domanda e non ho amato il mongoskin "soddisfacente" così ho fatto un po 'più di scavo e ho trovato monk. È costruito su mongoskin, riordina l'API e restituisce le promesse per tutte le chiamate asincrone. Probabilmente merita una sbirciatina per chiunque altro sbarca qui.