Вопрос

I would like to make a promise with the MongoDB driver. I wrote the following code:

var TaskBroker = function () {
  this.queueName = 'task_queue';
  this.rabbit = {};
  this.mongo = {};
};

TaskBroker.prototype.connectRabbit = function() {
  var self = this;

  return amqp.connect('amqp://localhost')
    .then(function(connection) {
      self.rabbit.connection = connection;
      return connection.createChannel()
    })
    .then(function(channel) {
      self.rabbit.channel = channel;
      return channel.assertQueue(self.queueName, {durable: true});
    })
};

TaskBroker.prototype.connectMongo = function() {
  console.log('Connect Mongo');
  var self = this;
  var defer = q.defer();
  MongoClient.connect('mongodb://127.0.0.1:27017/test', {}, defer.makeNodeResolver());
  return  defer.promise.then(function(db) {
    self.mongo.db = db;
    console.log('hello');
    return 42;
  });
};

TaskBroker.prototype.connect = function () {
  var self = this;
  return this.connectRabbit()
    .then(self.connectMongo);
};

Do you have any idea why I don't have the output hello when I call the method connect:

taskBroker.connect()
  .then(function(result) {
    console.log('Disconnected');
    taskBroker.disconnect();
});
Это было полезно?

Решение

Manually promisifying an API is dangerous, I suggest something along the lines of:

TaskBroker.prototype._connectMongo = Q.nfcall(MongoClient.connect,
                                             'mongodb://127.0.0.1:27017/test',
                                            {});
TaskBroker.prototype.connectMongo = function(){
   return this._connectMongo().then(function(db){
       console.log("Hello");
       // self.stuff...
       return 42;
   }).catch(function(e){
       console.err("connection error",e); // log the connection error, or handler err
       throw e; // don't mark as handled, propagate the error.
   });
};

With Bluebird promises, that'd look something like:

var MongoClient = Promise.promisifyAll(require("mongodb").MongoClient);

TaskBroker.prototype.connectMongo = function(){
    return MongoClient.connectAsync().then(... 
        // Bluebird will automatically track unhandled errors        
};

Другие советы

v2.0.36 of the node mongodb driver introduced first-class support for promises.

Here's an example from the official docs:

// A simple query showing skip and limit using a Promise.

var MongoClient = require('mongodb').MongoClient,
  test = require('assert');
MongoClient.connect('mongodb://localhost:27017/test', function(err, db) {

  // Create a collection we want to drop later
  var collection = db.collection('simple_limit_skip_query_with_promise');
  // Insert a bunch of documents for the testing
  collection.insertMany([{a:1, b:1}, {a:2, b:2}, {a:3, b:3}], {w:1}).then(function(result) {

    // Peform a simple find and return all the documents
    collection.find({})
      .skip(1).limit(1).project({b:1}).toArray().then(function(docs) {
        test.equal(1, docs.length);
        test.equal(null, docs[0].a);
        test.equal(2, docs[0].b);

        db.close();
    });
  });
});

By default, the es6-promise library is used, but you can override this when creating your DB connection:

var MongoClient = require('mongodb').MongoClient;

MongoClient.connect('mongodb://localhost:27017/test', {
  promiseLibrary: require('bluebird')
}, function(err, db) {
  // ...

MongoDB

For handling the mongodb driver, I suggest using then-mongo (disclaimer: I wrote it). This is equivalent to mongojs except with promises instead of callbacks. It also lets you treat the "connect" operation as being synchronous by immediately returning an object you can interact with, and just waiting for the connection internally. The really nice thing is that it matches the official mongodb documentation so you can just use that documentation to figure out how to use it.

General Case

In general, taking an API that does not return a promise and getting one that does should be done using the Promise constructor. e.g.

function readFile(filename, enc){
  return new Promise(function (fulfill, reject){
    fs.readFile(filename, enc, function (err, res){
      if (err) reject(err);
      else fulfill(res);
    });
  });
}

If you're using Q you should do:

function readFile(filename, enc){
  return q.promise(function (fulfill, reject){
    fs.readFile(filename, enc, function (err, res){
      if (err) reject(err);
      else fulfill(res);
    });
  });
}

Helper Methods

Lots of libraries, Q included, provide special helper methods for adapting node.js style callback methods to return promises. e.g.

var readFile = q.denodeify(fs.readFile);

or with promise:

var readFile = Promise.denodeify(fs.readFile);

If you want more info on how to create and consume Promise objects in general (rather than Q specific) I would suggest you check out https://www.promisejs.org/ (diclaimer: I wrote it).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top