Loading is disappearing and come back

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

can I know why inside query2.trigger are we triggering other queries ? can we make for example also for query3 onSuccess to trigger query4?

for example query2, depends on query1, query3 depends on query2 which method would you suggest

I'm having an issue because queries are called in unordered way

query1.trigger({                              //start query1
  onSuccess: function(data) {         //query1 finished successfully                                                       
    query2.trigger({                          //start query2
      onSuccess: function(data){      //query2 finished successfully
        query3.trigger({                      //start query3
          onSuccess: function(data){  //query3 finished successfully
           
          }
        }
      }
    }
  }
}));

Should I put inside async function or leave like this as you sent me

if await is not used, you do not need async. shouldvl be good to go

2 Likes

what do you think is this good approach to go with

async function processClients() {
loading.setValue(true)
try {
await fetchClients.trigger();
await extractRealClients.trigger();
await calculateStatistics.trigger();
await fetchClientsPrevYear.trigger();
await extractRealClientsPrevYear.trigger();
await calculateStatisticsPrevYear.trigger();
await calculatePercentages.trigger();
await fetchClientDetails1Year.trigger();
await extractThreeLastMonths.trigger();
await statisticsThreePrevMonths.trigger();
console.log("All operations completed successfully.");
} catch (error) {
console.error("An error occurred during the process:", error);
} finally {
loading.setValue(false)
}
}

// Make sure to call the function to execute the async process.
processClients();

that should fix your loading flicker/disappearing problem. let me know if its not working as expected

yes it working properly just a bit worried if any of queries fails will this code perform okay

no it probably won't be ok. if one of those queries fails the queries after it will still run. you'll need to there was a bug at one point that kept this code from working, there's been no update on the post tho. you could do the promise.all() if you want to stop running queries if one of them fails or you can wrap them all up in a promise w resolve/reject tokens passed to onSuccess/onFailure.

async function processClients() {
loading.setValue(true)

let querylist = [];
querylist.push(fetchClients.trigger());
querylist.push(extractRealClients.trigger());
querylist.push(calculateStatistics.trigger());
querylist.push(fetchClientsPrevYear.trigger());
querylist.push(extractRealClientsPrevYear.trigger());
querylist.push(calculateStatisticsPrevYear.trigger());
querylist.push(calculatePercentages.trigger());
querylist.push(fetchClientDetails1Year.trigger());
querylist.push(extractThreeLastMonths.trigger());
querylist.push(statisticsThreePrevMonths.trigger());

Promise.all(querylist).then(() => {
  console.log("All operations completed successfully."));
}.catch (error) {
  console.error("An error occurred during the process:", error);
}.finally {
loading.setValue(false)
}
function triggerQuery(query) {
  return new Promise((resolve, reject) => {
    query.trigger({
      onSuccess: resolve,
      onFailure: reject
    })
  })
}

async function processClients() {
  loading.setValue(true)
  try {
    await triggerQuery(fetchClients);
    await triggerQuery(extractRealClients);
    await triggerQuery(calculateStatistics);
    await triggerQuery(fetchClientsPrevYear);
    await triggerQuery(extractRealClientsPrevYear);
    await triggerQuery(calculateStatisticsPrevYear);
    await triggerQuery(calculatePercentages);
    await triggerQuery(fetchClientDetails1Year);
    await triggerQuery(extractThreeLastMonths);
    await triggerQuery(statisticsThreePrevMonths);
    
    console.log("All operations completed successfully.");
  } catch (error) {
    console.error("An error occurred during the process:", error);
  } finally {
    loading.setValue(false)
  }
}

// Make sure to call the function to execute the async process.
processClients();
2 Likes

I went with second approach and it seems to work properly I failed one query with intention to see if other queries will be called or not

1 Like

maybe out of context but I want to store jwt in retool where can I save it and make it reusable thanks

Hey @dinkok!

You can store and reuse JWTs in Retool using the provided OpenID Connect (OIDC) integration. This requires either an ID token or access token to be a JWT. Here's how to implement it:

1. Access settings in Retool and navigate to Authentication > SSO.
2. Click on 'Add SSO' and select 'OpenID Connect (OIDC)'.
3. Fill out the required fields that include your application's OAuth client ID and secret, scopes to grant to Retool, and the authorization and token endpoints for your OpenID provider.

Retool will then attempt to decode these tokens as JWTs. Once stored, they're available within Retool to customize applications or control permissions. Access them using {{current_user.metadata.idToken}} or {{current_user.metadata.accessToken}}.

If a refresh token was returned in the initial login flow, it's automatically used by Retool to refresh the tokens every two hours. This can be customized!

Here are some docs that might be helpful:
https://docs.retool.com/sso/quickstarts/custom-oidc

https://docs.retool.com/sso/quickstarts/custom/oidc#thin-tokens-and-fat-tokens

this would require the Enterprise plan right, or is it possible to make the Google SSO act the same way? if it does require the Enterprise plan, does Retool have any other secure built-in storage for JWTs or is the best option still a HttpOnly cookie (local/session storage can be unsafe usually, but that would be for the browser storage and not necessarily the Retool local/session storage... if that makes sense? someone had to point out the difference to me otherwise I'd still think they were the same thing.)

Thank you for chiming in here! You're correct that Retool's OpenID Connect (OIDC) integration, which facilitates storing and reusing JWTs, is indeed only available on the Enterprise Plan. I don't know of any other natively supported storage options for JWTs on other plans, but I certainly could be missing something!

Re: Retool localStorage, it just uses the regular ol' window localStorage, so same risks would apply: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

1 Like