Knex's modify
function is meant for synchronous composition:
Allows encapsulating and re-using query snippets and common behaviors as functions. The callback function should receive the query builder as its first argument, followed by the rest of the (optional) parameters passed to modify.
Making it generally asynchronous has a practical problem and also a semantic problem. For the practical problem, as in your example, the return value of callback.apply
is not observed or returned: If the function is async
or it otherwise returns a Promise, there's no way to signal to you that it's done. The semantic problem is that ordering matters (for example, when passing multiple orderBy
clauses). Knex's syntax is full of synchronous operations, not Promises; the assumption is that the query produced by Knex is available synchronously.
Furthermore, as a practical challenge in the language, it is difficult to return a Knex query builder as a result of a Promise because Knex builders have a Promise-like then
function. Promise-resolving systems in JavaScript (including Promise.resolve and async
function returns) will accept an object with a then
function as a "thenable", or a foreign promise implementation, which the Promise system further unwraps. Consequently, returning a Knex promise builder is not possible without a wrapper Array or Object, or else JS will call then
on the builder and execute the query.
All that said, there's nothing preventing you from creating your own modifyAsync
function with the same semantics as modify
, which waits for the passed asynchronous callback function and then returns a Promise that resolves to the wrapped modified query builder.
async function modifyAsync(queryBuilder, asyncCallback, ...args) {
await asyncCallback.call(queryBuilder, queryBuilder, ...args);
// Wrap in an array to prevent JS from calling queryBuilder.then.
return [queryBuilder];
}
async function createQuery() {
let builder = knex.table(yourTable)
/* ... */;
// Destructure using an array.
[builder] = await modifyAsync(builder, yourAsyncModifyFunction, 'foo', 'bar');
// Further modify the builder.
builder.orderBy(/* ... */);
// Return the builder, again through an array to avoid the async
// function automatically calling `then`.
return [builder];
}