My Posts

The defer function in Laravel

Exploring the defer() Function in Laravel: A Practical Guide

Laravel has always been at the forefront of making developers’ lives easier by introducing features that simplify complex tasks. One such recent addition is the defer() function. Introduced in the Laravel 11.23 release, its a game-changer for handling non-blocking operations in your application. In this post, we’ll explore how the defer() function works and implement a practical example where a user’s request count is incremented without delaying their response.


Why defer()?

In many web applications, there are scenarios where certain operations need to happen in the background after a user’s request is processed. For example:

  • Logging user activity
  • Sending emails
  • Making API calls

Traditionally, these tasks were handled using Laravel’s queues. While queues are powerful, they can feel like overkill for simple tasks. This is where the defer() function shines. It allows you to execute code after the HTTP response is sent to the user’s browser, improving response times and user experience.


How Does defer() Work?

The defer() function wraps the code you want to execute after the response is sent. Here’s a simple breakdown of how it works:

  1. The user makes a request.
  2. Laravel processes the request and sends the response back to the browser.
  3. Any code wrapped in the defer() function runs after the response is sent.

Additionally, Laravel provides an ->always() method to ensure the deferred code runs even if the request fails.


Practical Implementation: Incrementing User Request Count

Let’s implement a practical example where we increment a user’s request count every time they make a request. This operation doesn’t need to block the response, making it a perfect use case for the defer() function.

Step 1: Set Up the Database

Add a request_count column to your users table to track the number of requests each user has made.

php artisan make:migration add_request_count_to_users_table --table=users

In the migration file:

public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->unsignedInteger('request_count')->default(0);
    });
}

Run the migration:

php artisan migrate

Step 2: Implement the defer() Function

In your controller, wrap the code to increment the request count inside the defer() helper function.

use Illuminate\Support\Facades\Auth;

public function handleRequest()
{
    // Simulate processing the user's request
    $data = [
        'message' => 'Request processed successfully!'
    ];

    // Send the response to the user
    response()->json($data)->send();

    // Defer the increment operation
    defer(function () {
        $user = Auth::user();
        if ($user) {
            $user->increment('request_count');
        }
    });
}

Step 3: Test the Implementation

  1. Log in as a user.
  2. Make a request to the route handled by the handleRequest method.
  3. Check the request_count column in the database to see if it incremented after the response was sent.

Exploring the ->always() Method

Laravel’s defer() function includes an ->always() method that ensures the deferred code runs even if the request fails. This is particularly useful for tasks that must be executed regardless of the outcome, such as logging or cleanup operations.

Example of ->always() Usage

defer(function () {
    // Code to execute after response
    Log::info('Deferred task executed.');
})->always(function () {
    // Code that always runs, even if the request fails
    Log::info('This always runs, even on failure.');
});

In this example:

  • The first closure runs after the response is sent.

  • The second closure runs regardless of whether the request was successful or encountered an error.

This ensures critical operations are always performed, adding a layer of reliability to your application.


Benefits of Using defer()

  1. Improved User Experience: Users receive faster responses as heavy tasks are deferred.
  2. Simplicity: No need to set up queues or workers for simple background tasks.
  3. Error Handling: Use the ->always() method to ensure deferred tasks run even when the request fails.

Conclusion

The defer() function is designed for simplicity and efficiency, but it is not a replacement for queues. It runs tasks in the same PHP process, which means it is ideal for lightweight operations that don’t require extensive processing or external dependencies.

The defer() function is a powerful addition to Laravel’s toolkit, making it easier to handle non-blocking operations without the overhead of queues. In this post, we demonstrated how to use defer() to increment a user’s request count after sending the response. This is just one example of how defer() can streamline your application’s performance while keeping your code clean and efficient.

Start exploring the defer() function in your projects and experience the difference it can make!