Loading is disappearing and come back

I guess there will be a few milliseconds where none of the queries are actually fetching - if you want to bundle them up as a single transaction I'd suggest you track the loading state as a temporary variable that you set to true before triggering the first query and set it to false onSuccess of the last query.

can you make an example how to do it

yea, ok, try this:
forum (4).json (24.2 KB)

got you thanks

For example I have 5 queries and they all need to be called together and I dont want to each queries to go to add success: and select corresponding queries what need to be called after that querie is any way to make a script and call all of queries onSuccess: after each one

Ok, there's 2 things I think you could mean here by "called together":

  1. I want 5 queries to run in sequence and when the 5th one has completed I want to make the process as complete
  2. I want 5 queries to run at the same time in parallel and when all 5 have completed I want to mark the process as complete

My example above is the first option of these and the usual way to handle errors or to chain queries is to use the Success handler of the query to trigger the next one.

If your intent is the second option then you could do this with a javascript function that waits for each query to complete - The query.trigger() command returns a javascript Promise, and you can have multiple Promises running at the same time.

Take a look at this forum post that explains how you can async run a batch of promises (ie queries) and await for all of them to complete

Or this one that shows how to run multiple queries and return them as one result set

There's a lot of example on the forum of doing this kind of work.
There's also a great page in the docs about it

1 Like

just for a simple example if you choose to go the javascript route with promises:

const queries = [];
queries.push(myQuery.trigger({additionalScope: {i: 'stuff'}));
queries.push(myQuery2.trigger());
return Promise.all(queries);

technically Retool will wait untill all promises are resolved before it finishes running so you could leave out the last line i guess. If you need to do something after they all run you can extend this a bit with:

const queries = [];
queries.push(myQuery.trigger({additionalScope: {i: 'stuff'}));
queries.push(myQuery2.trigger());
  
queries.then((ret) => {
  console.log(ret) // ret is an array of return values for each promise [resolvedValue1, resolvedValue2...]
}).catch((err) => {
  console.log(err) // this will be the reject reason for the 1st rejected promise only
}
1 Like

is this safe to go? or manually to go and add query to success section
isAnythingLoading.setValue(true);
async function triggerQueriesSequentially() {
try {
// Trigger query1 and wait for it to complete
await query1.trigger();

// Once query1 completes successfully, trigger query2
await query2.trigger();

// Once query2 completes successfully, trigger query3
await query3.trigger();

// After all queries have successfully completed, set isAnythingLoading to false
isAnythingLoading.setValue(false);

} catch (error) {
// Handle any errors that occur during the execution of the queries
console.error("Error during query execution:", error);
}
}

// Call the function to execute the queries in sequence
triggerQueriesSequentially();

this code will work, but it is possible there might be a bug in it. isAnythingLoading.setValue(false); returns a promise, so as it is if you use that variable before this code block returns it won't contain the new value. you can either change it to await isAnythingLoading.setValue(false); or you can remove async and change the code to use chaining

// Once query1 completes successfully, trigger query2
await query2.trigger();

// Once query2 completes successfully, trigger query3
await query3.trigger();

// After all queries have successfully completed, set isAnythingLoading to false
isAnythingLoading.setValue(false);

// at this point isAnythingLoading still has a value of 'true'
// this isn't a big deal if this code ends/returns after calling .setValue()
// otherwise I'd suggest the code below
function triggerQueriesSequentially(){
  try{
    query2.trigger().then(() => {
      query3.trigger().then(() => {
        isAnythingLoading.setValue(false).then(() => {
          //use the new value of isAnythingLoading here
        }
      }
    }
  }
  catch(err){
    console.log(err);
  }
}

what you think should I call queries in async way or go and add manually in success section below for each query

as long as you use await isAnythingLoading.setValue(false); you'll be fine. the code also looks cleaner and is easier to debug. I usually only use the chaining when I'm super worried about the value of a variable i just updated.

I want to make sure because most of the queries I have are not async and they are called from success section for each query and its taking time until each query is finishing what you think should I leave as it is or change all of them in that format with async calling them together?

ah, if none of the queries depend on the result of one of the other queries you can do

const queryList = [];
queryList.push(query2.trigger());
queryList.push(query3.trigger());
Promise.all(queryList).then(() => {
  isAnythingLoading.setValue(false);
}

this would be instead of using await and it's a bit more compact. not sure why i didn't include this possibility earlier :man_shrugging:

some queries depends on others some not

then ya I'd stick with what you posted using await, just change that one line to await isAnythingLoading.setValue(false) to be safe (or live dangerously and go for it lol, it'll work just fine if that's the last line)

but what you think what is the safest to go with calling manually from success section where i chose which query to trigger and select or with async

easiest and safest? calling from Success event handler. fastest? probably async, but you can make it just as safe.

if you're extremely worried about the fastest but would rather do things like retool for safety I'd probably suggest going back to Promise.all():

const queryList = [];
queryList.push(query2.trigger({
  onSuccess: function(data) {
    query3.trigger(); //needs results from query2
    query4.trigger(); //needs results from query2 but doesn't care about query3
  },
  onFailure: function(data) {
    //we need to do something specific if query2 fails.
  }
}));

//query5 doesn't care about query1,2 or 3.
queryList.push(query5.trigger({
  onSuccess: function(data) {
    query6.trigger(); //needs results from query5 but doesn't care about 1,2 or 3 either.
  }
}));

Promise.all(queryList).then(() => {
  //all queries are successful
  isAnythingLoading.setValue(false);
}.catch((error) => {
  //at least one promise failed, we can handle that here
  console.log(error);
});

the difference here is that query2 and query5 can more or less start at the same time. we're also using the Success and Failure hooks that the UI uses so we know all the error handling is taken care of. we don't care which query failed so we use .catch() on Promise.all() instead of using the onFailure hook. if you need to do something in response to a specific query failing you can add the onFailure hook like i did for query2

if i just confused you way too much you can use the await one u had

on my drive home i just realized what you probably meant by safest (the least likely to have that loading problem)... if thats the case then ya just go with

await query2.trigger();
await query3.trigger();
await isAnythingLoading.setValue(false);

sorry for so many repies. i think i should probably summarize since ive given you so many different ways to do 1 thing, and each with their pros and cons.

lets start witht the down and dirty itll work one:

await query2.trigger();
await query3.trigger();
await isAnythingLoading.setValue(false);

why is this the down and dirty way? well it works, but its possible to see little or 0-ish i.prove t in speed.
PROS:

  • easy to understand
  • easy to debug and either rule rule out the code as causing a problem or not
  • easy to maintain. the most expensive part of software development is last part, maintenance
  • its safe, itll fix the loading error u had

CONS:

  • This is the same as using the Successful event handlers and doing everything manually. the only diff is you're bypassing some the Retool overhead for a slight speed increase. it could also be worse. using the HI to set the Success event handler
  • its not flexible and so requires a try/catch
const queryList = [];
queryList.push(query2.trigger({
  onSuccess: function(data) {
    query3.trigger(); //needs results from query2
    query4.trigger(); //needs results from query2 but doesn't care about query3
  },
  onFailure: function(data) {
    //we need to do something specific if query2 fails.
  }
}));

//query5 doesn't care about query1,2 or 3.
queryList.push(query5.trigger({
  onSuccess: function(data) {
    query6.trigger(); //needs results from query5 but doesn't care about 1,2 or 3 either.
  }
}));

Promise.all(queryList).then(() => {
  //all queries are successful
  isAnythingLoading.setValue(false);
}.catch((error) => {
  //at least one promise failed, we can handle that here
  console.log(error);
});

PROS;

  • it'll be faster
  • it'is safe, the loading thing will be fixed
1 Like

thank you so much for those solutions