Accessing query data within trigger().then does not work

I use .trigger to execute an API query and "then" to sequentially access to the values returned by the query.

Here an example:

query63.trigger().then
(
() => TemporaryStateCategoryUID.setValue(query63.data.id)
)

I figured out, that the data returned by query63.data.id is outdated when I try to access it. It doesn't reflect the data generated by the latest query63.trigger() call. If I use .then, I would expect that first Retool is executing the query query63 and then in a second step I can access the data created in query63 by using .then (resp. .data). However, it seems like the "storing" of the data generated by the query query63 happens somehow async in Retool and is not really part of the .trigger function.

Hi @Reboon!

I'm not seeing the synchronous behavior you're looking for with .then, but async/await syntax works for me. Can you give something like the below a shot? It logs the actual output from that particular query run once it completes.

async function triggerGetData() {
  let data = await getData.trigger();
  console.log(data);
}
triggerGetData();

-Justin

Hi @jmann

Thanks for the script. I was testing it and figured out the following behavior.

  1. This works. It is your script, but printing out the total row count.
async function triggerGetData() {
  let data = await GetDataFromLongRunningAPICall.trigger();
  console.log(data.total);
}
triggerGetData();
  1. This does not work. Here I am accessing the data of the query with .data after it's execution.
async function triggerGetData() {
  await GetDataFromLongRunningAPICall.trigger();
  console.log(GetDataFromLongRunningAPICall.data.total);
}
triggerGetData();

I would expect that the second script works as well since I want to access the data of the query after it has been prepare with .trigger().
I also verified whether the sync flow is correct. And yes it seems correct, first GetDataFromLongRunningAPICall is being executed (runs in my case like 6 seconds), after this has been completed the console.log runs. However, in the second query the data.total is empty.

I further tried a more complex version of the query.
As you can see in this query below, I am calling GetDataFromLongRunningAPICall same as before. But then I call QueryDataRunsAsWellAndWorksFine which fills the text component text1 with GetDataFromLongRunningAPICall.data.limit.
The same I do after 5 seconds with text2.setValue(GetDataFromLongRunningAPICall.data.limit);.

async function triggerGetData() {
  await GetDataFromLongRunningAPICall.trigger();
  QueryDataRunsAsWellAndWorksFine.trigger();
  await new Promise(r => setTimeout(r, 5000));
  text2.setValue(GetDataFromLongRunningAPICall.data.limit);
}
text1.setValue(0);
text2.setValue(0);
triggerGetData();

I would expect that text1 as well as text2 are being filled with the value from GetDataFromLongRunningAPICall.data.limit. But it is not, text1 is correctly filled. text2, however, contains the outdated data from GetDataFromLongRunningAPICall.trigger.
My conclusion so far is that Retool must somehow store a copy of GetDataFromLongRunningAPICall.data during run-time of a query and does not update this which is the reason why the data is outdated.

I also attached a link to a video that clearly shows the behavior.

And here the JSON file of the app I was testing with in the video (the API call needs to be replaced accordingly).

Test Ticket 13318 - V2.json (23.8 KB)

@jmann were you able to reproduce my issue?
Here another simple example which I just encountered:

query88 (type JS Query):

query89.trigger().then
(
  () => utils.exportData(query89.data, 'test file', 'xlsx')
);

query89 (type: Query JSON with SQL):
select 'test rrrrrrrrrrrrrrrr'

If you run the above query query88, the export will be empty, because it can't access the data from query89 even though they were created before the export was called.

Actually I don't even think we need the async function to synchronously access the results after triggering a query, does just using await like in the below not work for you?

let data = await getData.trigger();
utils.exportData(data, 'test', 'xlsx');

In terms of the other example, like I saw originally, I'm not seeing the then syntax working, so let me know if I'm missing something on why just using await doesn't work here!

1 Like

I have tried your suggestion, and yes it does work indeed. But the problem is, the other way around it should work too which is clearly doesn't.
I made another example to illustrate the problem:

See here your code (first two lines), which does work properly. Now you see a third line, this line is accessing the data from getData by using *.data. This should work in my opinion, but it doesn't. *.data simply does not return any data (if the app was opened freshly and getData.trigger never ran before).

let data = await getData.trigger();
utils.exportData(data, 'test1', 'xlsx'); // this is working
utils.exportData(getData.data, 'test2', 'xlsx'); // this is not working

Or how do you see it? Shouldn't getData.data be available with data?
Because here another interesting example:

First you start this:

let data = await getData.trigger();
await new Promise(r => setTimeout(r, 30000));
utils.exportData(getData.data, 'test 10', 'xlsx');

While the above is still running, you start the below query:
utils.exportData(getData.data, 'test 11', 'xlsx');

What happens: The second query will finish right away and creates the Excel correlcty. The first query finishes within around 30 seconds and, surprise, the Excel won't create (because the query considers getData.data as empty, even though getData.trigger() ran before).

This proofs that getData.data is not available inside the query that started getData.trigger() but for all other queries, that started after getData.trigger() getData.data is available and filled with data.

Do you know what I mean?