Mongoose 将数据从 withTransaction 助手中传递出去
Posted
技术标签:
【中文标题】Mongoose 将数据从 withTransaction 助手中传递出去【英文标题】:Mongoose pass data out of withTransaction helper 【发布时间】:2020-06-12 19:31:49 【问题描述】:简介
你好,
我正在尝试从 mongoose withTransaction
回调传递数据。现在,我正在使用以下实现回调的代码:
const transactionSession = await mongoose.startSession()
await transactionSession.withTransaction(async (tSession) =>
try
// MARK Transaction writes & reads removed for brevity
console.log("Successfully performed transaction!")
cb(null, "Any test data")
return Promise.resolve()
catch (error)
console.log("Transaction aborted due to error:", error)
cb(error)
return Promise.reject()
)
catch (error)
console.log(error)
return cb(error)
可以找到here 使用的withTransaction
助手的更详细的sn-p。
可以在 here 找到有关 withTransaction
助手的官方 Mongoose 文档的链接。
目前,我正在使用回调从withTransaction
callback 传递数据:
cb(null, "Any test data")
但是,问题在于,在返回 Promise.resolve()
之前,自然会先执行回调。这意味着,(在我的情况下)在提交任何必要的数据库写入之前将成功响应发送回客户端:
// this is executed first - the callback will send back a response to the client
cb(null, "Any test data")
// only now, after the response already got sent to the client, the transaction is committed.
return Promise.resolve()
为什么我认为这是个问题:
老实说,我不确定。如果当时没有任何数据库写入,那么将成功响应发送回客户端是不合适的。有人知道处理这个特定用例的适当方法吗?
我考虑过使用类似这样的方法将数据从 withTransaction
助手中传递出来:
const transactionResult = await transactionSession.withTransaction(...)
我试过了,响应是 MongoDB 的 CommandResult
,其中不包括我在已解决的承诺中包含的任何数据。
总结
如果在提交事务之前将成功响应发送回客户端,是否有问题?如果是这样,从withTransaction
助手传递数据并因此在发送回响应之前提交事务的适当方法是什么?
如果我得到任何建议,我将不胜感激。
【问题讨论】:
【参考方案1】:对于如何正确使用 Promises,这里似乎存在一些混淆,在几个层面上。
Callback 和 Promise 使用不当
如果函数应该接受回调,则不要返回 Promise。如果该函数应该返回一个 Promise,请使用 Promise 给出的回调:
const transactionSession = await mongoose.startSession()
await transactionSession.withTransaction( (tSession) =>
return new Promise( (resolve, reject) =>
//using Node-style callback
doSomethingAsync( (err, testData) =>
if(err)
reject(err);
else
resolve(testData); //this is the equivalent of cb(null, "Any test data")
);
)
让我们更详细地看一下:
return new Promise( (resolve, reject) =>
这会创建一个新的 Promise,Promise 会为您提供两个回调函数供您使用。 resolve
是表示成功的回调。您将要返回的对象传递给它。请注意,我已经删除了 async
关键字(稍后会详细介绍)。
例如:
const a = new Promise( (resolve, reject) => resolve(5) );
a.then( (result) => result == 5 ); //true
(err, testData) =>
该函数用于将 Node 样式的 cb(err, result)
映射到 Promise 的回调。
try/catch 使用不正确。
Try/catch 只能用于同步语句。让我们比较一下同步调用、Node 样式(即cb(err, result)
)异步回调、Promise 和使用 await:
try
let a = doSomethingSync();
catch(err)
handle(err);
异步:
doSomethingAsync( (err, result) =>
if (err)
handle(err);
else
let a = result;
);
承诺:
doSomethingPromisified()
.then( (result) =>
let a = result;
)
.catch( (err) =>
handle(err);
);
等待。 Await 可以与任何返回 Promise 的函数一起使用,并让您像处理同步代码一样处理代码:
try
let a = await doSomethingPromisified();
catch(err)
handle(err);
附加信息
Promise.resolve()
Promise.resolve()
创建一个新的 Promise 并使用未定义的值解析该 Promise。这是以下的简写:
new Promise( (resolve, reject) => resolve(undefined) );
与此等效的回调是:
cb(err, undefined);
async
async
与 await
搭配使用。如果您在函数中使用await
,则该函数必须声明为async
。
就像await
解包一个 Promise(resolve
成一个值,reject
成一个异常),async
包装 代码成一个 Promise。 return value
语句被翻译成Promise.resolve(value)
,抛出的异常throw e
被翻译成Promise.reject(e)
。
考虑下面的代码
async () =>
return doSomethingSync();
上面的代码等价于:
() =>
const p = new Promise(resolve, reject);
try
const value = doSomethingSync();
p.resolve(value);
catch(e)
p.reject(e);
return p;
如果您在没有await
的情况下调用上述任何一个函数,您将得到一个Promise。如果你await
其中任何一个,都会返回一个值,否则会抛出异常。
【讨论】:
您好,非常感谢您的详细回答!你完全正确,我不是 JS 承诺方面的专家。您能否告诉我以我的方式返回 Promise 和您的方式(使用新的 Promise(resolve,reject))之间的区别是什么?这两种方式都会告诉 withTransaction 助手它是否有效,即是否应该提交数据库交互。另外,在将响应发送回客户端之前,我现在如何能够提交数据库写入?您能否提供一些示例代码?非常感谢:) @linus_hologram 当然,给我一点时间,我会编辑答案 @linus_hologram 我添加了一些关于Promise.resolve()
的信息。但我意识到,如果你给我更多关于你编辑的部分的详细信息,对你的帮助会更容易
嘿,当然!我会链接一个要点,给我几分钟。
嘿,请看更新的答案。您可以在第一个代码 sn-p 下方找到指向要点的链接 :)以上是关于Mongoose 将数据从 withTransaction 助手中传递出去的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 React 前端从 Mongoose 显示带有 <img> 的图像
将 Mongoose 数据库中的内容上传到 React 组件
将 apollo 服务器与 mongodb mongoose 连接
Angular 2 + NodeJS + Mongoose - 从 Angular 2 发布数据到 rest api nodejs 后端。节点 api 日志:选项 /url,POST 标头不起作用