在 Knex.js 中插入 transaction.commit 之后执行的项



我想在完成事务之前在事务中并行执行插入。我使用了 Promise.all() 和 bluebird 承诺,如果一个承诺失败,就会取消所有承诺。问题是承诺似乎在实际执行插入之前就结束了。我正在使用Knex.js。我有两个承诺,一个在用户表中插入用户的用户名和电子邮件,另一个加密用户密码并在登录表中插入电子邮件和加密密码。

我找到了承诺和插入的执行顺序。他们以这种方式执行。(承诺插入用户名和电子邮件得到解析) -> (Knex 调试器说运行了用户名和电子邮件的插入命令) -> (承诺比插入电子邮件和密码得到解析) -> (transaction.commit) -> (Knex 调试器说运行了电子邮件和密码的插入命令,但事务已经结束并抛出错误)。这里的问题显然是电子邮件和密码承诺在执行电子邮件和密码插入之前执行

const addUser = (username, email, password) => {
return db.transaction(trx => {
let addLoginEntry = Promise.resolve(bcrypt.hash(password, 10)
.then(secret => {
trx("login").insert({
email: email,
secret: secret
}).then(console.log("PASSWORD INSERTED"));
})
);
let addUserEntry = Promise.resolve(
trx("users").insert({
username: username,
email: email
})
.then(console.log("USER INFO INSERTED"))
)
Promise.all([
addLoginEntry,
addUserEntry
])
.then(args => {
console.log("All promises done");
trx.commit(args);
})
.catch(error => {
[addLoginEntry, addUserEntry].forEach(promise =>promise.cancel());
console.log(error);
trx.rollback();
});
});
}

我希望同时更新登录表和用户表,但由于事务提交发生在将登录更新添加到事务之前,因此仅更新了用户表。以下是我在 Knex 中使用 debugging=true 运行程序时收到的错误消息:

USER INFO INSERTED
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'testemail@test.com', 'test' ],
__knexQueryUid: '2b1d59b1-1246-4237-87f1-d3fbfff7ba80',
sql: 'insert into "users" ("email", "username") values (?, ?)',
returning: undefined }
PASSWORD INSERTED
All promises done
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings:
[ 'testemail@test.com',
'$2b$10$D.qlOo7aDv4WCzssXGXuQeXQ3lZwWZ1.b1CRIn4DuSD.6ov.jzhBm' ],
__knexQueryUid: 'e3afdc4a-53bd-4f0d-ad71-7aab0d92d014',
sql: 'insert into "login" ("email", "secret") values (?, ?)',
returning: undefined }
Unhandled rejection Error: Transaction query already complete, run with DEBUG=knex:tx for more info
at completedError (C:PATH_TOnode_modulesknexsrctransaction.js:338:9)
at C:PATH_TOnode_modulesknexsrctransaction.js:304:24
at Promise.cancellationExecute [as _execute] (C:PATH_TOnode_modulesbluebirdjsreleasedebuggability.js:335:9)
at Promise._resolveFromExecutor (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:488:18)
at new Promise (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:79:10)
at Client_PG.trxClient.query (C:PATH_TOnode_modulesknexsrctransaction.js:300:12)
at Runner.query (C:PATH_TOnode_modulesknexsrcrunner.js:136:36)
at C:PATH_TOnode_modulesknexsrcrunner.js:40:23
at tryCatcher (C:PATH_TOnode_modulesbluebirdjsreleaseutil.js:16:23)
at C:PATH_TOnode_modulesbluebirdjsreleaseusing.js:185:26
at tryCatcher (C:PATH_TOnode_modulesbluebirdjsreleaseutil.js:16:23)
at Promise._settlePromiseFromHandler (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:517:31)
at Promise._settlePromise (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:574:18)
at Promise._settlePromise0 (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:619:10)
at Promise._settlePromises (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:699:18)
at Promise._fulfill (C:PATH_TOnode_modulesbluebirdjsreleasepromise.js:643:18)

您在那里缺少一个return语句,并且您的调试打印代码也有错误。我添加了注释来解释那里发生的事情:

return db.transaction(trx => {
let addLoginEntry = Promise.resolve(bcrypt.hash(password, 10)
.then(secret => {
// ---- MISSING RETURN HERE and PASSWORD INSERTED
// actually runs before query is even executed.
// should be .then(() => console.log("PASSWORD INSERTED"))
// to make that debug print line to be evaluated as part of
// promise chain
trx("login").insert({
email: email,
secret: secret
}).then(console.log("PASSWORD INSERTED"));
})
);
// also here USER INFO INSERTED is logged before
// query is even executed during evaluating query builder
// method parameters
let addUserEntry = Promise.resolve(
trx("users").insert({
username: username,
email: email
})
.then(console.log("USER INFO INSERTED"))
)
// at this point of code USER INFO INSERTED is already printed
// user add query is ran concurrently with bcrypt call and then 
// this is resolved and execution continues ....
Promise.all([
addLoginEntry,
addUserEntry
])
.then(args => {
// .... continues here and concurrently also login insert query is 
// created and PASSWORD INSERTED log is printed out
console.log("All promises done");
// for some reason .commit() gets executed before login insert query is
// actually triggered. It could have also worked correctly with
// some luck.
trx.commit(args);
})
.catch(error => {
[addLoginEntry, addUserEntry].forEach(promise =>promise.cancel());
console.log(error);
trx.rollback();
});
});

所以是的,基本上只缺少一个return语句。

最新更新