DB operations don't execute when promise is not awaited

The following snippet will not actually execute the SQL update:
function setEmailVerifiedWithoutBlocking(id: string) {
// If you replace `void` by `await`, this works
void db.update(myTable).set({ emailVerified: true }).where(eq(myTable.id, id))
}
function setEmailVerifiedWithoutBlocking(id: string) {
// If you replace `void` by `await`, this works
void db.update(myTable).set({ emailVerified: true }).where(eq(myTable.id, id))
}
My understanding is that this is because the promise is not chained by then, catch, or finally. This has nothing to do with the DB schema nor with the presence of the void keyword in front of it. I understand that this is part of a feature because sometimes you may want to do toSQL or prepare, in which case we do not want to execute the operation immediately. However the above situation represents a legit use-case (running a DB operation without blocking the current function), and the result is almost certainly not intended behaviour. I am aware of work-arounds like chaining an empty .finally(() => {}) or calling .execute() explicitely, but I thought I'd report this bug anyways because it may affect others.
4 Replies
Angelelz
Angelelz11mo ago
This is intended behavior. As you have stated, drizzle implements a type of lazy promise, that are only executed when awaited. You have also shared the way it's intended to be used when you need to actually execute the promise The execute method exists for this reason
Tomer Moran
Tomer Moran11mo ago
Ok, thanks. I think this behaviour is potentially problematic. I'll see if I can write a custom eslint rule to catch this
Angelelz
Angelelz11mo ago
Let me know what you come up with. We added an eslint plugin to help with this type of stuff. You can consider submitting a PR
Tomer Moran
Tomer Moran8mo ago
Hello again. I am using the no-restricted-syntax rule for simplicity, so I wrote the following AST selector:
UnaryExpression[operator="void"] MemberExpression[object.name="db"]
UnaryExpression[operator="void"] MemberExpression[object.name="db"]
It's not 100% precise since it can potentially have false-positives, and it also assumes Drizzle is used via a variable called db, but good enough for my purposes Leaving this here in case it helps others. Update: I've fixed the above ES query to the following:
UnaryExpression[operator="void"] > CallExpression > MemberExpression[property.name!="execute"] CallExpression MemberExpression:matches([object.name="db"], [object.name="tx"])
UnaryExpression[operator="void"] > CallExpression > MemberExpression[property.name!="execute"] CallExpression MemberExpression:matches([object.name="db"], [object.name="tx"])
This matches expressions where void is used on a chain of functions on db or tx, which doesn't end with .execute(). I'm not going to submit a PR for this because there are a lot of edge cases which are not accounted for here, but this version is more precise than the one I posted before.
Want results from more Discord servers?
Add your server