Mongoose 将数据从 withTransaction 助手中传递出去

lin*_*ram 2 javascript mongoose mongodb node.js async-await

介绍

嘿,

我正在尝试从猫鼬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)
}
Run Code Online (Sandbox Code Playgroud)

withTransaction可以在此处找到正在使用的帮助程序的更详细片段。

withTransaction可以在此处找到有关帮助程序的官方 Mongoose 文档的链接。

目前,我正在使用回调从回调中传递数据withTransaction

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()
Run Code Online (Sandbox Code Playgroud)

为什么我认为这是一个问题:

老实说,我不确定。如果当时没有任何数据库写入,则向客户端发送成功响应是不正确的。有人知道处理这个特定用例的适当方法吗?

我想过withTransaction使用这样的东西从助手传递数据:

const transactionResult = await transactionSession.withTransaction({...})
Run Code Online (Sandbox Code Playgroud)

我试过了,响应是CommandResultMongoDB 的,它不包括我在已解决的承诺中包含的任何数据。

概括

如果在提交事务之前将成功响应发送回客户端,这是一个问题吗?如果是这样,从withTransaction助手传递数据从而发回响应之前提交事务的适当方法是什么?

我会很感激我得到的任何建议。

Cod*_*ing 7

关于如何在几个层面上正确使用 Promises,这里似乎存在一些混淆。

回调和 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")
            }
        });
    })
Run Code Online (Sandbox Code Playgroud)

让我们更详细地看一下:

return new Promise( (resolve, reject) => {这将创建一个新的 Promise,并且 Promise 为您提供了两个回调以供使用。resolve是表示成功的回调。您将要返回的对象传递给它。请注意,我已经删除了async关键字(稍后会详细介绍)。

例如:

const a = new Promise( (resolve, reject) => resolve(5) );
a.then( (result) => result == 5 ); //true
Run Code Online (Sandbox Code Playgroud)

(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);
}
Run Code Online (Sandbox Code Playgroud)
  • 异步:
doSomethingAsync( (err, result) => {
    if (err) {
        handle(err);
    } else {
        let a = result;
    }
});
Run Code Online (Sandbox Code Playgroud)
  • 承诺:
doSomethingPromisified()
    .then( (result) => { 
        let a = result; 
    })
    .catch( (err) => {
        handle(err);
    });
Run Code Online (Sandbox Code Playgroud)
  • 等待。Await 可以与任何返回 Promise 的函数一起使用,并让您像处理同步代码一样处理代码:
try {
    let a = await doSomethingPromisified();
} catch(err) {
    handle(err);
}
Run Code Online (Sandbox Code Playgroud)

附加信息

Promise.resolve()

Promise.resolve()创建一个新的 Promise 并使用未定义的值解析该 Promise。这是以下的简写:

new Promise( (resolve, reject) => resolve(undefined) );
Run Code Online (Sandbox Code Playgroud)

与此等效的回调将是:

cb(err, undefined);
Run Code Online (Sandbox Code Playgroud)

async

asyncawait. 如果await在函数中使用,则该函数必须声明为async.

就像await将 Promise 展开(resolve变成一个值,然后reject变成一个异常)一样,async 代码包装到一个 Promise 中。一个return value语句被翻译成Promise.resolve(value),并抛出的异常throw e被翻译成Promise.reject(e)

考虑以下代码

async () => {
    return doSomethingSync();
}
Run Code Online (Sandbox Code Playgroud)

上面的代码等价于:

() => {
    const p = new Promise(resolve, reject);
    try {
        const value = doSomethingSync();
        p.resolve(value);
    } catch(e) {
        p.reject(e);
    }
    return p;
}
Run Code Online (Sandbox Code Playgroud)

如果你在没有 的情况下调用上述任何一个函数await,你将得到一个 Promise。如果您选择await其中任何一个,您将被返回一个值,或者将抛出异常。