Deferred Functions trong Laravel
Các phiên bản Laravel trước version 11, chúng ta thường sử dụng Queued Jobs
cho phép thực hiện sắp xếp các tác vụ xử lý background
.
Nhưng đôi khi có những tác vụ đơn giản mà chúng ta muốn defer
mà không cần cấu hình hoặc duy trì Queue Worker
trong thời gian dài.
Trong version Laravel 11, cung cấp cho chúng ta một hàm Helpers là Deferred Functions
cho phép chúng ta defer
thực thi closure
sau khi HTTP response được gửi tới người dùng, giúp ứng dụng của chúng ta có cảm giác nhanh và phản hổi nhanh chóng.
Tìm hiểu Deferred Functions trong Laravel
Để thử nghiệm tính năng Deferred Functions
trong Laravel. Đầu tiên, chúng ta sẽ tạo một command cơ bản với lệnh sau:
php artisan make:command TestDeferredFunctions
Command này sẽ giúp chúng ta có thể so sánh được các sự khác biệt giữa việc không sử dụng Deferred Functions
và sử dụng Deferred Functions
.
Không sử dụng Deferred Functions:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class TestDeferredFunctions extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:test-deferred-functions';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Demonstrates the defer functionality in a Laravel command';
/**
* Execute the console command.
*/
public function handle()
{
$this->info("Starting process.");
$this->delayedProcess();
$this->info("Finishing process.");
}
/**
* Delayed process function
*
* This method is invoked after a short delay to simulate a background task.
*/
protected function delayedProcess()
{
sleep(10); // Simulates a time-consuming operation
$this->info("Completed delayed process.");
}
}
Khi chạy lệnh này, chúng ta sẽ thấy kết quả như sau:
application@5e5ebc1e8817:/app$ php artisan app:test-deferred-functions
Starting process.
Completed delayed process.
Finishing process.
Sử dụng Deferred Functions:
Giờ đây, chúng ta sẽ điều chỉnh command trên để dùng Deferred Functions
bằng cách cập nhật hàm handle
như sau:
/**
* Execute the console command.
*/
public function handle()
{
$this->info("Starting process.");
defer(fn() => $this->delayedProcess());
$this->info("Finishing process.");
}
Bây giờ, khi chạy lại lệnh chúng ta sẽ thấy kết quả như sau:
application@5e5ebc1e8817:/app$ php artisan app:test-deferred-functions
Starting process.
Finishing process.
Completed delayed process.
Đối với yêu cầu HTTP thì Deferred Functions
sẽ không được thực hiện nếu status của HTTP response là 4xx
hoặc 5xx
.
Nhưng có thể thay đổi điều này bằng cách sử dụng method always
vào Deferred Functions
như sau:
Route::get('/test-deferred-functions', function () {
defer(function () {
// do time-consuming work here
sleep(10);
})->always();
return "Hello ManhDanBlogs";
});
Nếu bạn cần hủy một Deferred Functions
trước khi nó được thực thi, bạn có thể sử dụng method forget
để hủy Deferred Functions theo name
.
Chúng ta hãy cung cấp đối số thứ hai cho Illuminate\Support\defer
như sau để đặt name cho Deferred Functions:
defer(function () {
// do time-consuming work here
sleep(10);
}, 'name_defer');
defer()->forget('name_defer');
Sự kì diệu của Deferred Functions trong Laravel
Chúng ta đã tìm hiểu về Deferred Functions
thông qua các ví dụ minh họa phía trên, nhưng đối với HTTP requests chúng ta chưa giải thích được cách Deferred Functions
được thực hiện sau đó như thế nào phải không?
Thì điều kì diệu đó chỉ thực sự được xảy ra ở Global Middleware
mới được thêm vào Core Laravel:
\Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class
Nếu nhìn vào mã nguồn Middleware của InvokeDeferredCallbacks
thì nó đơn giản là một Terminable Middleware bình thường, nhưng chúng ta hãy để ý đến method terminate
sau:
/**
* Invoke the deferred callbacks.
*
* @param \Illuminate\Http\Request $request
* @param \Symfony\Component\HttpFoundation\Response $response
* @return void
*/
public function terminate(Request $request, Response $response)
{
Container::getInstance()
->make(DeferredCallbackCollection::class)
->invokeWhen(fn ($callback) => $response->getStatusCode() < 400 || $callback->always);
}
Method terminate
đang gọi DeferredCallbackCollection
chứa tất cả deferred callbacks được tích lũy trong yêu cầu hiện tại và sẽ được tự động gọi lại sau khi phản hồi về browser.
Đây chính xác là phương pháp mà Laravel có thể thực hiện được Deferred Functions
ngay sau khi phản hồi về phía Client.
Với Deferred Functions trong HTTP requests, Laravel đã thực sự tạo ra một phép thuật đặc biệt, giúp cải thiện hiệu suất và trải nghiệm người dùng một cách vượt bậc.