Tutorials

Retool Event Handlers Execution Order: Run Them Sequentially

OTC Team··4 min read
Retool Event Handlers Execution Order: Run Them Sequentially

If you've ever added multiple event handlers to a Retool button or component and noticed that your queries fire all at once, you've already run into the Retool event handlers execution order problem. By default, Retool runs all attached event handlers in parallel — there's no built-in toggle to make them execute sequentially, and there's no built-in "stop if previous handler fails" option. This catches a lot of builders off guard, especially when you're chaining queries that depend on each other's results or need to process records in a specific order.

Why Retool Runs Event Handlers in Parallel

Retool's event handler model is designed for speed and simplicity. For most UI interactions — like refreshing a table and sending a notification at the same time — running in parallel is exactly what you want. But when you need strict ordering (say, insert a record then send an email then refresh the table), parallel execution breaks your logic. There's a long-standing internal feature request at Retool to allow users to define execution order directly in the UI, but as of now, the workaround lives in JavaScript.

The Fix: Use a JS Query to Control Execution Order

Instead of stacking multiple event handlers on a component, consolidate all your logic into a single Run script event handler that calls a JavaScript query. Inside that JS query, you can use await to force sequential execution and handle failures gracefully.

Here's the core pattern:

  • Replace all your parallel event handlers with a single Run script handler or a dedicated JS query.
  • Use await query.trigger() to pause execution until each query resolves.
  • Wrap each step in a try/catch block so a failure stops the chain instead of silently continuing.
  • Use onSuccess callbacks if you prefer a callback-based approach over async/await.

Step-by-Step: Sequential Event Handlers in Retool

Step 1 — Remove your stacked event handlers. Go to the component (e.g. a Button) and delete all existing event handlers except one. This one remaining handler will be your single entry point.

Step 2 — Create a JavaScript query. In the query panel, create a new query, set the type to Run JS Code, and name it something like runSequentialSteps.

Step 3 — Write your sequential logic using async/await. Here's an example that runs three queries in order and stops if any one fails:

  • await query1.trigger();
  • await query2.trigger();
  • await query3.trigger();

In full, your JS query body would look like this:

async function runInOrder() {
  try {
    await query1.trigger();
    await query2.trigger();
    await query3.trigger();
  } catch (error) {
    console.error("Step failed, halting execution:", error);
  }
}
return runInOrder();

Step 4 — Point your event handler to this JS query. Back on your Button component, set the single event handler to Trigger query and select runSequentialSteps. Now when the button is clicked, each query fires one after the other, and the chain halts on any error.

Using onSuccess for Callback-Based Chaining

If you prefer not to use async/await, you can chain queries using the onSuccess event of each query. In the query settings for query1, add an onSuccess event handler that triggers query2, and so on. This creates a sequential chain without writing an explicit async function. The downside is that error handling is less centralized — you'd need to add onFailure handlers to each query individually to prevent the chain from continuing after a failure.

Real-World Use Case: Processing Bank Files in Order

One scenario that surfaces this problem frequently: importing multiple files that each need to be processed by a series of JavaScript transformations and SQL updates — in order, without overlap. If you trigger all the processing queries in parallel, records get written out of sequence and your data integrity breaks. The async/await pattern above solves this exactly. Wrap each file's full processing pipeline in a sequential JS query, and use a loop with await to process one file at a time.

What to Expect from Retool in the Future

Retool's team has acknowledged this gap and has an internal feature request open to allow builders to define execution order directly in the event handler UI — similar to how Superblocks handles it natively. Until that ships, the JS query approach is the most reliable and maintainable workaround available.

Key Takeaways

  • Retool fires multiple event handlers in parallel by default — there is no native sequential mode yet.
  • Use a Run JS Code query with async/await and try/catch to enforce execution order and stop on failure.
  • Alternatively, use onSuccess callbacks to chain queries one after another.
  • Consolidate all chained logic into a single entry-point event handler for cleaner, more debuggable apps.
  • A native UI solution is on Retool's roadmap — until then, JavaScript is your best friend here.

If you're building something more complex — like a multi-step data pipeline or a file import workflow — and the JS approach isn't cutting it, consider reaching out to a Retool agency that can architect the query and state management layer properly from the start.

Ready to build?

We scope, design, and ship your Retool app — fast.

Ready to ship your first tool?