I often use the following pattern to trigger queries in my javascript:
const updateSourceRow = async () => { // Make an async function
await qrySourceUpdate.trigger()
// Did it work?
const myField = qrySourceUpdate.data.theField[0]
await jsSetView.trigger({additionalScope: {skipSelect: true, myField }})
}
updateSourceRow() // Now trigger the function
The next line after the trigger executes whether the query failed or not. How do I know if the query failed? I can check if data === undefined or similar but that seems rather porous.
Okay, part 2. How do I return a value from an async/await patterned js query?
Let's modify the above query to:
// jsAsyncQuery
const updateSourceRow = async () => { // Make an async function
await qrySourceUpdate.trigger()
// Did it work?
const myField = qrySourceUpdate.data.theField[0]
return myField
}
return updateSourceRow() // Now trigger the function
console.log("invoice_id: " + invoice_id)
// 210701CUS2-2
await qryInvoicesSelect.trigger({additionalScope: {invoice_id}})
console.log("invoice_id (from query): " + qryInvoicesSelect.data.invoice_id[0])
// sometimes 210701CUS2-2 and sometimes undefined
I guess I should ask how do I get the data object returned as if I were using the onSuccess: function(data) {} pattern? That seems to always work.
Basically I am trying to figure out how to get all of the advantages of the "standard" pattern while getting the "nesting hell" killing advantages of the async/await pattern and I have realizing that I have the async/await pattern only halfway figured out.
You should be able to assign the call to query.trigger() to a variable. When the promise resolves, the variable will contain the data property of the query. Ie:
I've rewritten your example as an IIFE, but you can use a function expression and call if that is more comfortable, it should work the same way.
The reason why your example is returning undefined is because when a query is run, we essentially pass it a "snapshot" of your retool app, and this is what is used for any references to the app model such as query results/component values. Thus, it is getting the value of qrySourceUpdate.data.theField[0] prior to qrySourceUpdate being run.
I use the function expression pattern because frankly I'm old and set in my ways and I find the verbose ways more readable. Also, syntactic sugar rots your teeth and get off my lawn! - also I can use it this way to get some validation checking before executing the function:
const templateInsert = async () => {
currentTemplate.setValue(txtAddTemplateName.value)
await qryTemplateInsert.trigger() // Insert record
}
if (txtAddTemplateName.value) { // Only execute if form is filled in.
templateInsert() // Now trigger the function
}
Anyhoo, any advice on Part 1 and 2?
Edit: Of course I can also do this and keep my syntax sweet:
if (txtAddTemplateName.value) { // Only execute if form is filled in.
(async () => {
const res = await qrySourceUpdate.trigger()
const myField = res.theField[0]
return myField
})();
}
So for part 1, i think that the solution really would be to check if the return is non-null, as simple as that seems. So expanding on your example, you could do something like this:
if (txtAddTemplateName.value) { // Only execute if form is filled in. (async () => { const res = await qrySourceUpdate.trigger() if (res) { const myField = res.theField[0] return myField } else { throw new Error("Query returned no data!") } })(); }
For part 2, I think there may actually be a bug with async/await in Retool, as what you are doing should be correct. I was able to use a Promise.resolve() to do this however:
Checking return type was my plan B after seeing your first email. And thanks for the workaround for part 2.
But what this forced me to do was level up my game in T-SQL stored procedures. I ended up doing all of the logic there which was really the right thing to do. Separation of concerns, increased performance and all that.
But I still need some fancy js queries and this levels me up there as well.
I'll make an exception, you can walk on my lawn any time you want!