Step 11 — Background work
Ada clicks Place order. She should see success in a blink. Sending a “thanks for your order” email, pinging analytics, or resizing an image can wait a second — they should not hold the HTTP response hostage.
What is background work? (in general)
Most API code is synchronous: the client waits until your method finishes.
Background work means: finish the important part, send the response, then do the slow leftover tasks.
| Do now (in the request) | Do after (background) |
|---|---|
| Create the order row | Send confirmation email |
| Debit the wallet | Notify a warehouse Slack channel |
Return order_id | Rebuild a search index |
If email is down for thirty seconds, Ada should still have a placed order — not a spinning browser.
There are levels of “after”:
- Same process, after response — good for quick logs and best-effort mail (
defer()in Pionia). - Dedicated job queue — better when work must survive crashes and retries (RoadRunner Jobs, Redis queues, etc.).
This step uses level 1. Level 2 is documented under Background work.
What you will learn
- Call
defer()after a successfulorder.place - Know when a real job queue is worth it
- Step 9 — logged-in customer
Defer after checkout
Sketch (after you have OrderService + order/wallet tables):
#[Authenticated]
protected function placeAction(Arrayable $data): ApiResponse
{
$orderId = /* create order + items, debit wallet … */;
defer(function () use ($orderId) {
logger()->info('Send order confirmation', ['order_id' => $orderId]);
// call your mail provider later
});
return response(0, 'Order placed', ['order_id' => $orderId]);
}defer() schedules the closure to run after the response is sent (FPM can finish the request to the client first). Ada’s phone already shows success while your log/email runs.
When to graduate to a queue
Use RoadRunner Jobs (or similar) when:
- The work must retry if the mail API fails
- It must not be lost if the PHP worker restarts
- It is heavy (PDF invoices, large imports)
Until then, defer() is enough to keep checkout snappy while you learn.
Common mistakes
- Doing slow I/O before
return response(...)— Ada waits for no good reason - Assuming
defer()is a new thread with infinite time — keep closures short - Enabling
[jobs]without a running RoadRunner jobs worker — you get sync fallback + warnings