This is probably only useful if you're writing query builder, but I asked myself if it was possible to write a class that would construct an object and not have to expose something like a run()
method. Turns out it is!
So, the goal is to let us write something like:
const query = new UserQuery()
query
table('users')
.where('name', 'John')
.where('age', 31)
const results = await query
Most libraries would have us write await query.execute()
or similar, but the beauty of thenables let's us circumvent this. How?
Our base class
/**
* A base class that only executes once, once you `await` it.
* You could call it a Lazy Singleton Promise.
*/
class ThenableExecutor {
// Implement your own execute() method like this one.
/* abstract */ execute() {
return new Promise((resolve) => {
console.log('Promise is running');
setTimeout(() => { resolve('ResolvedValue') }, 1500)
})
}
_hasExecuted = false
_promise
then(resolve) {
if (!this._hasExecuted) {
this._hasExecuted = true
this._promise = this.execute()
}
resolve(this._promise)
}
}
module.exports = exports = ThenableExecutor
This base class does two things;
- it calls
execute()
when someone interacts with it as a promise, i.e. by usingawait
orthen
- It makes sure you only call the execute function once, no matter how many times you
await
it
The query builder
Now let's use our ThenableExecutor class to write a class that will construct the query up until the point when you run it.
const ThenableExecutor = require('./ThenableExecutor')
/**
* A sample query builder that only executes the query once you await it
*/
class PaymentUpdateActionRequest extends ThenableExecutor {
constructor(paymentId, version) {
super()
this.endpoint = `/payments/${paymentId}`
this.version = version
}
actions = []
execute() {
console.log(
'Making 1 HTTP request',
JSON.stringify({
url: this.endpoint,
method: 'POST',
headers: {
Authorization: `Bearer 1337`,
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
},
body: {
version: this.version,
actions: this.actions
}
})
)
return { some: 'data', from: 'backend' }
}
table(tableName) {
this.actions.push({
action: 'setKey',
key
})
return this
}
where(field, value) {
this.actions.push({
action: 'setInterfaceId',
interfaceId
})
return this
}
}
module.exports = exports = PaymentUpdateActionRequest