Mon goose передача данных из помощника транзакции - PullRequest
1 голос
/ 28 февраля 2020

Введение

Привет,

Я пытаюсь передать данные из обратного вызова mon goose 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)
}

Более подробный фрагмент используемого хелпера withTransaction можно найти здесь .

Ссылку на официальную документацию пн goose, касающуюся помощника withTransaction, можно найти здесь .

В настоящее время я использую функцию обратного вызова для передачи данных. из 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()

Почему я думаю, что это проблема:

Честно говоря, я не уверен. Просто неправильно посылать ответ об успехе клиенту, если в это время не было записи в базу данных. Кто-нибудь знает подходящий способ справиться с этим конкретным c сценарием использования?

Я думал о передаче данных из withTransaction помощника, используя что-то вроде этого:

const transactionResult = await transactionSession.withTransaction({...})

Я попробовал это, и ответ - CommandResult MongoDB, который не включает в себя какие-либо данные, которые я включил в разрешенное обещание.

Резюме

Это проблема, если ответ об успешной отправке возвращается клиенту до совершения транзакции? Если да, то каким образом можно передать данные от withTransaction помощника и тем самым совершить транзакцию до отправки ответа?

Буду благодарен за любой совет, который получу .

1 Ответ

2 голосов
/ 02 марта 2020

Похоже, здесь есть некоторая путаница относительно того, как правильно использовать Обещания на нескольких уровнях.

Обратный вызов и Обещание используются неправильно

Если функция должна принимать обратный вызов, не возвращайте Обещание. Если функция должна вернуть Обещание, используйте обратный вызов, данный Обещанием:

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) => { Это создаст новое Обещание, и Обещание дает вам два обратных вызова для использования. resolve является обратным вызовом для указания успеха. Вы передаете ему объект, который хотите вернуть. Обратите внимание, что я удалил ключевое слово async (подробнее об этом позже).

Например:

const a = new Promise( (resolve, reject) => resolve(5) );
a.then( (result) => result == 5 ); //true

(err, testData) => { Эта функция используется для сопоставления стиля узла cb(err, result) с обратными вызовами Promise.

Try / catch используются неправильно.

Try / catch может использоваться только для синхронных операторов. Давайте сравним синхронный вызов, асинхронный обратный вызов в стиле узла (то есть cb(err, result)), обещание и использование await:

  • Синхронный:
try {
    let a = doSomethingSync();
} catch(err) {
    handle(err);
}
  • Asyn c:
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() создает новое Обещание и разрешает это Обещание с неопределенным значением. Это сокращение для:

new Promise( (resolve, reject) => resolve(undefined) );

Эквивалент обратного вызова этого будет:

cb(err, undefined);

async

async идет с await. Если вы используете await в функции, эта функция должна быть объявлена ​​как async.

Так же, как await разворачивает Обещание (resolve в значение и reject в исключение), async оборачивает код в Обещание. Оператор 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, вы вернется обещание. Если вы await любой из них, вам будет возвращено значение, или будет сгенерировано исключение.

...