Promises 传播中间对象 (NodeJS + MongoDB)

Promises propagating intermediate objects (NodeJS + MongoDB)

我正在尝试在 Node 4.x

中使用 MongoDB 和 Promises

在这个例子中我想:

  1. 连接到我的 mongodb
  2. 然后使用给定的密钥删除所有内容
  3. 然后插入一条记录
  4. 然后关闭连接

幸运的是,当您不给它回调时,mongodb 客户端会吐出承诺。这是我想出的。

const MongoClient = require('mongodb').MongoClient;
const test = require('assert');

function insertDoc(doc, collName) {
  return MongoClient.connect('mongodb://localhost:27017/myDB')
    .then(db => {
      const col = db.collection(collName);
      return col.deleteMany({ 'Key': doc.key })
        .then(() => col.insertOne(doc))
        .then(result => test.equal(1, result.insertedCount))
        .then(() => db.close);
    });
}

代码似乎有效,但嵌套 .then() "feels" 错误。任何想法如何做到这一点,以便在我准备 .close() 时可以使用 db 对象?

就目前而言,您可以在外部作用域中使用变量来实现此目的:

let db;

function insertDoc(doc, collName) {
  return MongoClient.connect(dsn)
    .then(connectedDb => {
      db = connectedDb;
      return col.deleteMany(doc)
    }) // now you can chain `.then` and still use `db`
}

有一些可能的替代方案,例如传递 db,但这对我来说似乎很奇怪。如果你想保持这个流程但仍然利用异步性,你可以使用 async/await。现在你需要一个转译器,比如 babel 和 regenerator-runtime 之类的东西来使用它。

async function insertDoc(doc, collName) {
  const db = await MongoClient.connect(dsn);
  const col = db.collection(collName);
  await col.deleteMany({Key: doc.key});
  const result = await col.insertOne(doc);
  await test.equal(1, result.insertedCount) // is this asynchronous?
  return db.close();
}

您也可以使用 co/yield 来避免转译,尽管它有点冗长。

我发现您所拥有的是我见过的最易读的替代方案。我自己使用缩进(嵌套 .then's)来访问以前的值(这是我唯一一次这样做!)

很多事情最终都会影响代码的外观和阅读方式。以您的 col 临时文件为例。如果不需要,您的代码可能如下所示:

var insertDoc = (doc, collName) => MongoClient.connect('mongodb://localhost:x/DB')
  .then(db => db.collection(collName).deleteMany({ 'Key': doc.key })
    .then(() => db.collection(collName).insertOne(doc))
    .then(result => test.equal(1, result.insertedCount))
    .then(() => db.close))
  .then(() => doSomethingElse());

注意 db.close) 之后的额外 )。括号匹配的编辑器很有帮助。 :)

我并不是建议为了代码美观而删除 col。我之所以展示这个,是因为我认为它突出了缩进如何很好地显示 db 值的范围。当我看到这样的代码时,我开始喜欢这种模式。

在现实生活中,代码并不总是整齐地折叠,但我喜欢它在可能时折叠的模式。

一种选择是将承诺更多地视为值,然后在需要时提取包装的值。它有自己的可读性缺点。

例如

function insertDoc(doc, collName) {
  const db = MongoClient.connect('mongodb://localhost:27017/myDB');
  const col = db.then(db => db.collection(collName));

  return col.deleteMany({ 'Key': doc.key })
    .then(() => col.insertOne(doc))
    .then(result => test.equal(1, result.insertedCount))
    // You've still got the 'db' promise here, so you can get its value
    // to close it.
    .then(() => db.then(db => db.close()));
}