Queues & Jobs
Move slow work — sending email, resizing images — into the background so your pages stay fast.
What you will learn
- Explain why slow work belongs in a queue
- Create and dispatch a job
- Run a worker to process the queue
The problem: slow requests
Some tasks take a while — sending a welcome email, generating a PDF, resizing a big image. If you do them during the request, the user stares at a spinner until they finish. A queue fixes this: instead of doing the slow work now, you drop a job onto a waiting list and respond to the user instantly. A separate background process — a worker — picks jobs off the list and runs them afterwards.
- A job is a class describing one piece of slow work (e.g. "send the welcome email to user 5").
- A queue is the waiting list those jobs sit on.
- A worker is a long-running process that takes jobs off the queue and runs them.
Think of a restaurant: the waiter (your web request) takes your order and walks away immediately; the kitchen (the worker) cooks it in the background. The waiter does not stand frozen at your table until the food is ready.
Step 1 — create a job
Artisan generates the job class:
php artisan make:job SendWelcomeEmailThis creates app/Jobs/SendWelcomeEmail.php. The class has a handle() method — the code that actually does the slow work — and a constructor where you pass in any data the job needs:
// app/Jobs/SendWelcomeEmail.php
public function __construct(public User $user) {}
public function handle(): void
{
// the slow work runs here, in the background
Mail::to($this->user->email)->send(new WelcomeMail($this->user));
}The constructor public User $user stores the user the job is about (this neat syntax both declares and saves the property). handle() holds the actual work — here, sending the welcome email. Importantly, handle() does not run during the web request; it runs later, on the worker.
Step 2 — dispatch the job
From your controller you dispatch the job — push it onto the queue — and immediately move on:
// in the controller, right after creating the user
SendWelcomeEmail::dispatch($user);
return redirect('/dashboard'); // the user sees this instantlySendWelcomeEmail::dispatch($user) puts the job on the queue and returns straight away — it does not wait for the email to send. So the user is redirected to their dashboard in milliseconds, while the email goes out moments later in the background.
Step 3 — run a worker
Jobs only get processed if a worker is running. You start one in the terminal:
php artisan queue:workNote: Output:
INFO Processing jobs from the [default] queue.
App\Jobs\SendWelcomeEmail ........ RUNNING
App\Jobs\SendWelcomeEmail ........ 842ms DONE
The worker sits waiting; each time a job is dispatched it picks it up and runs handle(). In production you keep this running with a process monitor like Supervisor (or use Laravel Horizon for a dashboard).
The whole flow, end to end:
- A request comes in (e.g. a user just registered).
- The controller dispatches a job onto the queue and responds to the user immediately.
- The job waits in the queue (a database table or Redis).
- A running worker (
php artisan queue:work) picks the job up. - The worker runs the job’s
handle()method — the slow email actually sends.
Tip: Set QUEUE_CONNECTION=database in .env and run php artisan queue:table && php artisan migrate to use a simple database-backed queue while learning. If a job throws an error, Laravel can retry it, and permanently failed jobs land in a failed_jobs table you can inspect.
Q. Why dispatch slow work to a queue instead of doing it during the request?
✍️ Practice
- Create a job, dispatch it from a controller, and process it with
php artisan queue:work. - Add an artificial
sleep(3)inhandle()and confirm the page still responds instantly.
🏠 Homework
- Queue a “send welcome email” job that runs when a user registers, and watch it process in a worker.