Драйвер узла MongoDB 2.0.* с обещанием Bluebird 2.9.*

Итак, есть несколько других запросов по этой теме, таких как: bluebird">Как я могу обещать родной Javascript-драйвер MongoDB с помощью bluebird?

Однако, похоже, это не относится к последней версии драйвера, которая, похоже, имеет проблемы при попытке обещания. В настоящее время я могу заставить работать MongoClient, выполнив:

Promise.promisifyAll(mongodb.MongoClient); // Using .Prototype here fails to promisify

Однако независимо от того, что я пытаюсь сделать, коллекции, похоже, не работают с использованием вызовов *async, они могут вызывать их, но они никогда не разрешаются и не отклоняются, поэтому они просто зависают в подвешенном состоянии.

Исторически сложилось так, что в предыдущих версиях вы просто Promise.promisifyAll(mongodb) и все готово, но я не уверен, как правильно обрабатывать это в новом драйвере.

Вот пример вывода коллекции, которая была создана с использованием прямого обещания mongo connectAsync, а затем получения коллекции из возвращенной базы данных. Как только я пытаюсь что-то сделать с коллекцией, она просто зависает, и обещания не возвращаются из нее:

{ s: { pkFactory: { [Function: ObjectID] index: 14727641, createPk: [Function: createPk], createFromTime: [Function: createFromTime], createFromHexString: [Function: createFromHexString], isValid: [Function: isValid], ObjectID: [Circular], ObjectId: [Circular], createPkAsync: [Object], createFromTimeAsync: [Object], createFromHexStringAsync: [Object], isValidAsync: [Object], bindAsync: [Object], toStringAsync: [Object], callAsync: [Object], applyAsync: [Object], lazyAsync: [Object], throttleAsync: [Object], debounceAsync: [Object], delayAsync: [Object], everyAsync: [Object], cancelAsync: [Object], afterAsync: [Object], onceAsync: [Object], fillAsync: [Object] }, db: { domain: [Object], _events: {}, _maxListeners: undefined, s: [Object], serverConfig: [Getter], bufferMaxEntries: [Getter], databaseName: [Getter], options: [Getter], native_parser: [Getter], slaveOk: [Getter], writeConcern: [Getter] }, topology: { domain: [Object], _events: [Object], _maxListeners: undefined, connectTimeoutMS: 500, s: [Object], bson: [Getter], isMasterDoc: [Getter], poolSize: [Getter], autoReconnect: [Getter], host: [Getter], port: [Getter], emitOpen: false, socketTimeoutMS: 0 }, dbName: 'some-db-name', options: {}, namespace: 'some-namespace', readPreference: null, raw: undefined, slaveOk: false, serializeFunctions: undefined, internalHint: null, collectionHint: null, name: 'some-collection-name' } }


person Grofit    schedule 18.03.2015    source источник
comment
Для всех, кто смотрит на это, в Mongo 2. * они, кажется, меняют то, что возвращается из определенных методов, например, findAsync теперь, кажется, возвращает некоторые огромные модели, а не документы, которые я получал раньше, так что эта проблема на полпути между необходимостью переноса логики и ее правильным обещанием.   -  person Grofit    schedule 19.03.2015


Ответы (4)


Вы можете промисифицировать его сразу после запроса, как показано в документах bluebird API, например:

var Promise = require("bluebird");
var MongoDB = Promise.promisifyAll(require("mongodb"));
var util = require('util');

console.log(util.inspect(MongoDB, { showHidden: true }));

Используя bluebird 2.9.14 и драйвер mongodb 2.0.22, я получил следующие (упрощенные) результаты:

  // ....
  Collection: 
   { [Function]
     [length]: 6,
     [name]: '',
     [arguments]: [Getter/Setter],
     [caller]: [Getter/Setter],
     [prototype]: 
      { [constructor]: [Circular],
        collectionName: [Getter],
        // .... 
        findAsync: [Object],
        insertOneAsync: [Object],
        insertManyAsync: [Object],
        bulkWriteAsync: [Object],
        insertAsync: [Object],
        updateOneAsync: [Object],
        replaceOneAsync: [Object],
        updateManyAsync: [Object],
        updateAsync: [Object],
        deleteOneAsync: [Object],
        removeOneAsync: [Object],
        deleteManyAsync: [Object],
        removeManyAsync: [Object],
        removeAsync: [Object],
        saveAsync: [Object],
        findOneAsync: [Object],
        // ....

И успешно запрошен так:

MongoDB.connectAsync('mongodb://localhost:27017/test').then(function(db) {
    return db.collection("orders").findOneAsync({});
}).then(function(orders) {
    console.log(orders);
}).catch(function(err) {
    console.log(err);
});

ОБНОВЛЕНИЕ

Использование объекта MongoClient также работает:

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

MongoClient.connectAsync('mongodb://localhost:27017/test').then(function(db) {
    return db.collection("orders").find({}).toArrayAsync();
}).then(function(orders) {
    console.log(orders)
}).catch(function(err) {
    console.log(err);
});
person victorkt    schedule 18.03.2015
comment
Я делаю вышеописанное и вижу, что для некоторых вещей есть асинхронные методы, но объект MongoClient содержит только connect, а не connectAsync. Однако в вашем примере вы подразумеваете, что подключаетесь напрямую к объекту mongodb, а не к объекту mongodb.MongoClient, который указан в документации: mongodb.github.io/node-mongodb-native/2.0 можете ли вы снова запустить свой код и вывести объект MongoClient, тогда это может пролить свет на этот вопрос, как и остальная часть можно связать нормально, но connectAsync просто не существует, используя приведенный выше пример. (с MongoClient) - person Grofit; 19.03.2015
comment
Я могу подтвердить, что вышеуказанное соединение через базовый объект mongodb работает таким образом, но другие объекты по-прежнему ведут себя не так, как ожидалось. Например, я подключаюсь сейчас, используя способ, показанный выше, а затем создаю коллекцию, объект которой не похож на ваш. Я опубликую редактирование с выводом объектов моей коллекции. - person Grofit; 19.03.2015
comment
Я обновил свой ответ примером с использованием объекта MongoClient. Вывод, который я разместил, был из базового объекта MongoDB, а не из db.collection(). Тем не менее, я мог запросить базу данных, как обычно, используя функции *Async. - person victorkt; 19.03.2015
comment
Я собираюсь отметить ваш ответ как правильный, так как кажется, что здесь есть кое-что еще. поскольку версия 2. * добавляет некоторые новые методы и немного меняет поведение существующих, поэтому я думаю, что у меня возникла проблема с некоторыми вещами, такими как remove, не работающими должным образом, но если я перейду к deleteOne или deleteOneAsync, он будет работать правильно, поэтому вы правы в своих утверждениях, однако я думаю, что всем, у кого есть аналогичная проблема, нужно будет подумать о переносе своих асинхронных вызовов на более новые. Спасибо еще раз за помощь. - person Grofit; 19.03.2015

По умолчанию драйвер mongodb всегда возвращает обещание, если вы не укажете обратный вызов. Но вы можете указать ему возвращать промисы, используя предпочитаемую вами библиотеку промисов.

Вот простой способ использования промисов bluebird при использовании драйвера node-mongodb-native 2.0:

var Promise = require("bluebird");
var MongoClient = require("mongodb").MongoClient; // Doesn't require promisification

/*...*/
function saveData(data) {
  MongoClient
    .connect(MONGO_CONNECTION_STRING, {
      promiseLibrary: Promise // Here you instruct to use bluebird
    })
    .then(function(db) {
      return db
        .collection('myCollection')
        .insert(data)
        .finally(db.close.bind(db))
    })
    .catch(function(err) {
      console.error("ERROR", err);
    });
}
person David Rissato Cruz    schedule 07.10.2015
comment
Обратите внимание и на это примечание: bluebirdjs.com/docs/api/resource-management.html - person Tom; 05.12.2015
comment
Если вы используете поиск, вам нужно нажать на курсор, прежде чем окончательно закрыть базу данных, но это кажется лучшим решением. - person conradj; 27.06.2016
comment
Да, вы должны потреблять его перед закрытием. На самом деле было бы предпочтительнее оставлять соединения открытыми, потому что подключаться и отключаться при каждом запросе довольно дорого. Я просто попытался создать своего рода пример полного цикла, но я настоятельно рекомендую повторно использовать ваши соединения. Драйвер Mongo может почти прозрачно управлять пулом соединений. - person David Rissato Cruz; 27.06.2016

Обтекаемая и более реалистичная версия:

var Promise = require('bluebird');
var MongoDB = Promise.promisifyAll(require('mongodb'));

MongoDB.MongoClient.connectAsync('mongodb://localhost:27017/test')
  .then(function(db) { // Expose db to query logic
    // need to return a promise to let outer catch handle it
    return db.collection("orders").find({}).toArrayAsync()
      .then(function (orders) {
        console.log(orders);
      })
      // Ensure that db is closed at the end no matter what...
      .finally(db.close.bind(db)); 
      // No need for another catch here, the outer one will handle it 
  })
  .catch(console.log.bind(console));

Вложенность промисов делается специально, чтобы открыть базу данных для остальной логики. То же самое можно сделать без вложенности, передав или объявив 'db' глобально. Пробовали все, и этот самый элегантный.

person kornieff    schedule 12.09.2015

Вы можете прочитать источник собственного драйвера MongoDB:

MongoClient.connect = function(url, options, callback) {
  var args = Array.prototype.slice.call(arguments, 1);
  callback = typeof args[args.length - 1] == 'function' ? args.pop() : null;
  options = args.length ? args.shift() : null;
  options = options || {};

  // Get the promiseLibrary
  var promiseLibrary = options.promiseLibrary;

  // No promise library selected fall back
  if(!promiseLibrary) {
    promiseLibrary = typeof global.Promise == 'function' ?
      global.Promise : require('es6-promise').Promise;
  }

  // Return a promise
  if(typeof callback != 'function') {
    return new promiseLibrary(function(resolve, reject) {
      connect(url, options, function(err, db) {
        if(err) return reject(err);
        resolve(db);
      });
    });
  }

  // Fallback to callback based connect
  connect(url, options, callback);
}

promiseLibrary можно установить Bluebird

person Richard Xue    schedule 17.09.2016