How can your app finish work after someone closes the page?
Give the visitor a way to come back to the result. Save their request, hand the work to something that can run after the page closes, and show whether it is waiting, finished or needs another try.
When should someone be able to leave the page?
Move work to the background when the visitor can go on without the finished result. A report can take time to build. Checking whether someone may request it still needs to happen before the app accepts the request.
Tell the person that the request was saved and give them somewhere to see the outcome. If the queue is unavailable, show that the request could not be started or record it for a later handoff. A fast response only helps when the user knows what actually happened.
Which parts have to keep working?
A queue keeps work that is waiting. A worker is the code that takes the work and runs it. Your app still needs a place to keep the finished report and a rule about who can open it.
Some services run the worker for you. Others call code on your host or give your own worker the next job. A workflow can remember completed steps so a later retry can continue. Check what still runs when the web server restarts and where the waiting work and the final file live.
Compare tools
Scroll the table sideways to read every column.
| Tool | Where the code runsWho hosts the process that carries out the work. | What runs againHow the system retries failed work and what a manual restart can repeat. | When two requests become oneWhat the service can recognize and where your app still needs a rule. | What you pay forThe main units and limits that affect the bill. | Where to check the resultWhat you can inspect or keep after a job finishes. |
|---|---|---|---|---|---|
| Trigger.dev | Deploy TypeScript tasks separately. Cloud runs them on Trigger.dev machines. A self-hosted option puts that work on your infrastructure. | Automatic retries give the task another attempt. Replay creates a new run, usually on the current task version. | A task trigger key can return the existing run within its lifetime. A failed run clears that key. Make the report write safe to repeat. | Cloud bills the run starting and the seconds spent computing, using the chosen machine rate. Check included credit and simultaneous runs. | Use the run dashboard for attempts and logs. Cloud log history varies by plan. Keep reports in your app’s storage. |
| Inngest | Your web host or connected worker runs the code. Inngest coordinates its steps and keeps track of their results. | A failed step can retry using results already saved. A manual rerun can start again or reuse earlier step results. | A stable event ID can suppress duplicate starts within 24 hours. A retry inside a step can still repeat an outside action. | Cloud counts a workflow run plus its steps. Check the monthly plan and concurrent-step allowance, and add your host’s charges. | Inspect the run trace and its step results. Trace history and bulk replay availability have separate limits. Keep large files outside run state. |
| BullMQ | You run a worker and keep the queue in Redis or, with BullMQ v6, a supported PostgreSQL database. The library connects the two. | A configured retry runs the job again. A stalled job can return to waiting, and you can manually retry a retained failed job. | A custom job ID can block a second queue entry while that record exists. Removing it allows the ID to be used again. | The library has no per-job subscription. Pay for the worker, database and related storage, backup and monitoring. | Use queue state and worker events to investigate. Set how long to keep completed and failed jobs so their records fit the store. |
How do you test one report without touching customer data?
Use a disposable report and verify the whole path from request to a file you can reopen.
- Check the connections before startingConfirm that the test app, queue, worker, database and file storage use test-only settings. Use fake records and a disposable account. Leave real payments, outgoing email and automatic schedules disconnected. If you can’t confirm where the work goes, stop before adding a job.
- Request one report and leave the pageGive this report a stable request ID. Ask the test app to build it, close the page and reopen the test app. Check that the job has a clear status and that the finished file belongs to the test account that asked for it.
- Make a second fake report fail onceFor a new fake report, make the test worker fail once after saving the file and before it records success. Keep the same request ID when that job retries. Check that it reuses the saved file and leaves one usable result.
- Check the final failure stateMake one fake dependency keep failing until the configured attempts run out. Confirm the test app stops saying the report is still on its way and that you can see what failed before deciding whether to try again.
What should happen if a job fails or arrives twice?
Give each intended report one stable ID and have the database allow one result record per request ID. Save the complete report under one file name derived from that ID. Use storage that can create the file only if that name is unused. A retry can reuse the file and finish recording it on the same request.
A timeout can mean the first attempt did the work and lost the response. Check the existing result before repeating an outside action. Where the outside service supports a stable operation key, use the same key for every attempt at the same action. A queue’s duplicate check or a simple look before writing cannot protect every side effect.
| Situation | Next check |
|---|---|
| The job is waiting | Look at its waiting time and whether workers have room to start it. |
| An attempt failed | Read the reason and check if any result was already saved. Give temporary failures a limited number of retries with a delay. |
| The job reached its last attempt | Keep the failure visible. Fix the cause and check what a manual rerun would repeat. |
| The worker says it finished | Open the saved file through the test app and verify the intended user can reach it. |
What changes when reports run every week?
A schedule decides when work is due. Worker capacity affects when it starts. Track when each week’s report actually finishes.
Choose the time zone and check weeks when clocks change. Decide what to do if last week’s job is still running or the queue is unavailable. If a report is due once per customer and week, use that same period and customer to identify repeats.
Estimate a busy week using the provider’s actual units. Some services count machine time, others count runs and steps, and a library leaves you paying for your host and database. Add file storage and outside services to the budget.
What happens when you move jobs or remove an account?
Keep the customer’s finished work and your own record of the request separate from short-lived job logs. Decide how you will handle waiting jobs before switching tools.
For an account that no longer has access, stop future schedules and check access again before the worker reads private data or saves a new result. Cancel work where your setup supports it and reconcile actions already taken. Stopping a job does not undo a finished write or send.
Before moving, list waiting requests, schedules and saved reports. Check how much history you can retrieve. Prove the new worker can finish one fake request and decide which old jobs must finish, stop or move before the change.