Back to Blog
Lesson 14 of the Intermediate Laravel: Real-World Application Patterns course
LaravelJune 26, 20264 min read

Asynchronous Processing with Queues in Laravel

Improve application performance by offloading heavy tasks to queues. Learn how to configure drivers, create job classes, and dispatch background jobs.

LaravelQueuesPerformanceBackground JobsPHPbackend

Previously in this course, we explored Introduction to Laravel Events and Listeners to decouple our business logic. While events help organize code, they are synchronous by default—the listener runs immediately during the request. Today, we add asynchronous processing to our arsenal, allowing us to move heavy operations into the background to keep our API responses snappy.

Why Queues Matter for Performance

In a standard web request, the user waits for the server to finish every operation before receiving a response. If your project board API needs to generate a PDF report, send an email notification, or process a large image upload, the user is stuck staring at a loading spinner.

By using queues, we capture these tasks as "jobs" and store them in a database or a service like Redis. A background worker process picks these jobs up and executes them independently of the main request. This is the secret to scaling API design for asynchronous processing and maintaining low latency.

Configuring Your Queue Driver

Laravel abstracts the queue system through the QUEUE_CONNECTION setting in your .env file. For development, the sync driver is the default, which executes jobs immediately (blocking the request).

To move to true background processing, you should switch to database or redis. For our project board, let's use the database driver:

  1. Run php artisan queue:table to create the migration.
  2. Run php artisan migrate to create the jobs table.
  3. Update your .env: QUEUE_CONNECTION=database.

Once configured, any job you dispatch will be inserted into this table, waiting for a worker to process it.

Creating and Dispatching Job Classes

Laravel provides a simple artisan command to generate a job class: php artisan make:job ProcessTaskReport. This creates a class in app/Jobs.

Jobs are essentially plain PHP classes with a handle() method. This is where your heavy lifting resides. Here is how we define a job to handle a task-related report in our project board:

PHP
namespace App\Jobs;

use App\Models\Task;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class ProcessTaskReport implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public Task $task) {}

    public function handle(): void
    {
        #6A9955">// Simulate a heavy task, like generating a PDF
        sleep(5); 
        logger("Report generated for task: {$this->task->id}");
    }
}

The ShouldQueue interface is the critical piece; it tells Laravel that this class should be handled asynchronously rather than immediately.

Dispatching the Job

You trigger the job from your controller or service layer using the dispatch() method:

PHP
public function store(Request $request)
{
    $task = Task::create($request->validated());

    #6A9955">// Offload the heavy report generation
    ProcessTaskReport::dispatch($task);

    return response()->json(['message' => 'Task created, report processing.'], 202);
}

Hands-on Exercise

  1. Generate a Job: Create a job named SendTaskNotification that accepts a User model.
  2. Implement Logic: Inside the handle() method, use Log::info() to simulate sending an email.
  3. Dispatch: Trigger this job inside your TaskService after a task is successfully created.
  4. Test: Run php artisan queue:work in a separate terminal window and create a task via your API. Watch your terminal logs to see the job execute in the background.

Common Pitfalls

  • Forgetting the ShouldQueue Interface: If you omit this, your code will run synchronously, defeating the purpose of using queues.
  • Database Bloat: With the database driver, successful jobs remain in the table. Use php artisan queue:prune to clear out old, completed jobs.
  • Serialization Issues: When you pass a model to a job, Laravel uses SerializesModels. If you delete the model from the database before the job runs, the job will fail. Always ensure your data integrity logic accounts for this, as discussed in Database transactions for data integrity.
  • Worker Latency: If you don't run php artisan queue:work (or a supervisor process), your jobs will sit in the database forever. In production, always use a process monitor like Supervisor or Laravel Horizon.

Recap

Queues are your primary tool for improving performance in high-load applications. By configuring a driver like database, creating ShouldQueue classes, and dispatching them from your service layer, you decouple the user experience from heavy background work. When implemented alongside reliable background jobs, your application becomes significantly more resilient.

Up next: We will explore how to group these tasks together using Job Chaining and Batching to handle complex, multi-step workflows.

Similar Posts