Detect query failure when using async/await query.trigger() pattern

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

If I then trigger that query:

jsAsyncQuery.trigger()
// jsAsyncQuery.data === null

Part 3:

Take this code:

  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.

Hey @bradlymatthews,

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:

(async () => {
const res = await qrySourceUpdate.trigger()
const myField = res.theField[0]
return myField
})();

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.

Ahhh good that answers part 3.

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:

return Promise.resolve(targetQuery.trigger({
additionalScope : {key1: moment().format("ss")}
}))

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!

Glad to help! I will look into why returning with async/await seems to have issues, and follow up here if I figure it out!