Promises
Overview
Node.js 驱动程序使用异步 Javascript API 与 MongoDB 集群通信。
异步 JavaScript 允许您无需等待处理线程空闲即可执行操作。这有助于防止您的应用程序在执行长时间运行的操作时无响应。有关异步 JavaScript 的更多信息,请参阅有关异步 Javascript 的 MDN Web 文档。
本部分介绍了 Promises
,您可以将其与 Node.js 驱动程序一起使用,以访问针对 MongoDB 集群的方法调用的结果。
Promises
Promise 是异步方法调用返回的对象,它允许您访问有关它们封装的操作最终成功或失败的信息。如果操作仍在运行,则 Promise 处于待定状态;如果操作成功完成,则 Promise 处于已履行状态;如果操作引发异常,则 Promise 处于已拒绝状态。有关 Promise 和相关术语的更多信息,请参阅有关 Promise 的 MDN 文档。
与 MongoDB 集群通信的多数驱动程序方法(例如findOneAndUpdate()
和 countDocuments()
)都会返回 Promise 对象,并且已经包含处理操作成功或失败的逻辑。
您可以定义自己的逻辑,通过附加 then()
方法,一旦 Promise 达到 Fulfilled 或 Rejected 状态,该逻辑就会执行。then()
的第一个参数是 Promise 达到 Fulfilled 状态时调用的方法,可选的第二个参数是 Promise 达到Rejected 状态时调用的方法。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 文档。
Await 操作符
如果您使用 async
函数,则可以对 Promise 使用 await
操作符来暂停进一步执行,直到 Promise 达到 Fulfilled 或 Rejected 状态并返回。由于 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()
返回一个 Promise,解析为布尔值,指示是否存在其他结果);next()
返回一个 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()
。
如果我们将代码更改为仅 await
对hasNext()
调用(如下例所示),控制台将打印 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()); } }