承诺
概述
Node.js 驱动程序使用异步 JavaScript API 与您的 MongoDB 集群通信。
异步 JavaScript 允许您在不等待处理线程空闲时执行操作。这有助于防止您的应用程序在执行长时间运行的操作时变得无响应。有关异步 JavaScript 的更多信息,请参阅 MDN 网络文档中的异步 JavaScript.
本节描述了承诺
,您可以使用 Node.js 驱动程序使用它们来访问对 MongoDB 集群的方法调用结果。
承诺
承诺是由异步方法调用返回的对象,允许您访问它们包装的操作最终成功或失败的信息。如果操作仍在运行,承诺处于 挂起 状态,如果操作成功完成,则处于 已解决 状态,如果操作抛出异常,则处于 拒绝 状态。有关承诺和相关术语的更多信息,请参阅 MDN 文档中的 承诺。
大多数与您的 MongoDB 集群通信的驱动程序方法,例如 findOneAndUpdate()
和 countDocuments()
,返回承诺对象,并且已经包含处理操作成功或失败逻辑。
您可以通过附加 then()
方法来定义自己的逻辑,该逻辑在承诺达到 已解决 或 拒绝 状态时执行。then()
的第一个参数是在承诺达到 已解决 状态时调用的方法,而可选的第二个参数是在它达到 拒绝 状态时调用的方法。then()
方法返回一个承诺,您可以附加更多的 then()
方法。
当您将一个或多个 then()
方法附加到承诺时,每个调用都会将其执行结果传递给下一个调用。这种模式称为 承诺链式调用。以下代码示例通过附加单个 then()
方法演示了承诺链式调用的示例。
collection .updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } }) .then( res => console.log(`Updated ${res.result.n} documents`), err => console.error(`Something went wrong: ${err}`), );
要处理只到 拒绝 状态的承诺转换,请使用 catch()
方法而不是将第一个参数传递为 null
到 then()
。catch()
方法接受一个回调,当承诺转换到 拒绝 状态时执行。
catch()
方法通常附加在承诺链的末尾,以处理抛出的任何异常。以下代码示例演示了将 catch()
方法附加到承诺链末尾。
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达到已解决或拒绝状态并返回。由于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上关于await。
操作注意事项
在使用 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
。
如果我们修改代码,只对 next()
的调用使用 await
,如以下代码片段所示,它将抛出以下错误: 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()); } }
尽管 hasNext()
在 next()
返回结果后才被调用,但 hasNext()
的调用返回一个解析为 true
的 Promise,而不是它解析的值,类似于前面的示例。代码尝试在已经返回其结果并因此关闭的游标上调用 next()
。
如果我们修改代码,只对 hasNext()
的调用使用 await
,如以下示例所示,控制台将打印 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()); } }