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 文档的链接。

目前,我正在使用回调从withTransactioncallback 传递数据:

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

asyncawait 搭配使用。如果您在函数中使用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 助手中传递出去的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose 不会从 JSON 数组创建子文档

如何使用 React 前端从 Mongoose 显示带有 <img> 的图像

将 Mongoose 数据库中的内容上传到 React 组件

将 apollo 服务器与 mongodb mongoose 连接

Angular 2 + NodeJS + Mongoose - 从 Angular 2 发布数据到 rest api nodejs 后端。节点 api 日志:选项 /url,POST 标头不起作用

无法使用位置字段创建 Mongoose 对象