Docs 菜单

Docs 主页开发应用程序MongoDB 驱动程序Node.js 驱动程序

Promises

在此页面上

  • 概述
  • Promises
  • Await 操作符
  • 操作注意事项

Node.js 驱动程序使用异步 Javascript API 与 MongoDB 集群通信。

异步 JavaScript 允许您无需等待处理线程空闲即可执行操作。这有助于防止应用程序在执行长时间运行的操作时变得无响应。有关异步 Javascript 的更多信息,请参阅有关 异步 Javascript 的 MDN Web 文档 。

本部分介绍了 Promises,您可以将其与 Node.js 驱动程序一起使用,以访问针对 MongoDB 集群的方法调用的结果。

Promise 是异步方法调用返回的对象,它允许您访问有关它们包装的操作最终成功或失败的信息。如果操作仍在运行,则 Promise 处于“ 待处理 ”状态;如果操作成功完成,则 Promise 处于“已 履行 ”状态;如果操作引发异常,则 Promise 处于“已 拒绝 ”状态。有关 Promises 和相关术语的更多信息,请参阅有关 Promises 的 MDN 文档。

与 MongoDB 集群通信的多数驱动程序方法(例如findOneAndUpdate()countDocuments())都会返回 Promise 对象,并且已经包含处理操作成功或失败的逻辑。

您可以定义自己的逻辑,通过附加then()方法,一旦 Promise达到“已履行”或“已拒绝”状态,该逻辑就会执行。 then()的第一个参数是当 Promise 达到“已履行”状态时调用的方法,可选的第二个参数是当 Promise 达到“已拒绝”状态时调用的方法。 then()方法返回一个 Promise,您可以向其中附加更多then()方法。

当您将一个或多个then()方法追加到 Promise 时,每个调用都会将其执行结果传递给下一个调用。这种模式称为Promise 链接。以下代码段显示了通过追加单个then()方法进行 Promise 链接的示例。

collection
.updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } })
.then(
res => console.log(`Updated ${res.result.n} documents`),
err => console.error(`Something went wrong: ${err}`),
);

要仅处理到“拒绝”状态的 Promise 转换,请使用 catch() 方法,而不是将第一个参数 null 传递给 then()catch() 方法接受单个回调,该回调会在 Promise 过渡到 “拒绝”状态时执行。

catch()方法通常追加在 Promise 链接的末尾,以处理抛出的任何异常。以下代码段演示了将catch()方法追加到 Promise 链接的末尾。

deleteOne({ name: "Mount Doom" })
.then(result => {
if (result.deletedCount !== 1) {
throw "Could not find Mount Doom!";
}
return new Promise((resolve, reject) => {
...
});
})
.then(result => console.log(`Vanquished ${result.quantity} Nazgul`))
.catch(err => console.error(`Fatal error occurred: ${err}`));

注意

驱动程序中的某些方法(例如find() 会返回Cursor 而不是 Promise。要确定每个方法返回的类型,请参阅 Node.js API 文档。

如果您使用 async 函数,则可以对 Promise 使用 await 操作符来暂停进一步执行,直到 Promise 达到 FulfilledRejected 状态并返回。由于 await 操作符会等待 Promise 的解析,因此您可以使用它来代替 Promise 链以顺序执行逻辑。以下代码段使用 await 执行与第一个 Promise 链示例相同的逻辑。

async function run() {
...
try {
res = await myColl.updateOne(
{ name: "Mount McKinley" },
{ $set: { meters: 6190 } },
);
console.log(`Updated ${res.result.n} documents`);
} catch (err) {
console.error(`Something went wrong: ${err}`);
}
}

有关更多信息,请参阅有关 等待的 MDN 文档。

使用async方法时的一个常见错误是忘记对 Promise 使用await操作符来获取结果值,而不是 Promise 对象。考虑以下示例,我们使用hasNext()next()迭代游标,前者返回一个 Promise,解析为布尔值,指示是否存在更多结果,后者返回一个 Promise,解析为游标指向的下一个条目到。

async function run() {
...
// WARNING: this snippet may cause an infinite loop
const cursor = myColl.find();
while (cursor.hasNext()) {
console.log(cursor.next());
}
}

由于对 hasNext() 的调用会返回 Promise,因此无论解析出何值,该条件语句均会返回 true

如果我们将代码更改为 await,仅调用next()(如以下代码段所示),则会引发以下错误: MongoError: Cursor is closed

async function run() {
...
// WARNING: this snippet throws a MongoError
const cursor = myColl.find();
while (cursor.hasNext()) {
console.log(await cursor.next());
}
}

虽然在 next() 结果返回之后才会调用 hasNext(),但对 hasNext() 的调用会返回一个 Promise,而其计算结果为 true 而不是解析出的值(与前一示例类似)。此代码会尝试对已返回其结果并因此而关闭的 Cursor 调用 next()

如果我们将代码更改为仅 awaithasNext()调用(如下例所示),控制台将打印 Promise 对象而不是文档对象。

async function run() {
...
// WARNING: this snippet prints Promises instead of the objects they resolve to
const cursor = myColl.find();
while (await cursor.hasNext()) {
console.log(cursor.next());
}
}

hasNext()next() 方法调用之前使用 await 可确保对正确的返回值进行操作,如以下代码所示:

async function run() {
...
const cursor = myColl.find();
while (await cursor.hasNext()) {
console.log(await cursor.next());
}
}
← 指定增删改查操作在副本集上的运行方式