php-standard-library/async
Fiber-based async primitives for PHP: structured concurrency with cooperative multitasking. Run tasks concurrently, manage lifecycles, cancellations, and scopes predictably. Part of PHP Standard Library; docs and guides at php-standard-library.dev.
Installation
composer require php-standard-library/async
No additional configuration is required—Leverage Laravel’s autoloader.
First Use Case: Async Database Queries (Safe)
use Async\Task;
use Async\Promise;
use Illuminate\Support\Facades\DB;
// Wrap a synchronous DB query in an async task (offload to queue in production)
$promise = Task::run(function () {
return DB::table('users')->where('id', 1)->first();
});
// Await the result (blocking—use cautiously in Laravel routes)
$user = $promise->await();
return response()->json($user);
Where to Look First
Task class: The entry point for creating async tasks.
Task::run(), Task::later() (for delayed execution).Promise class: For composing and awaiting results.
Promise::all(), Promise::race(), then(), catch().Async facade: Laravel-friendly wrapper (if provided).
Async::task(fn() => ...)->await().// Fetch multiple API endpoints concurrently
$urls = ['https://api1.example.com', 'https://api2.example.com'];
$promises = collect($urls)->map(fn($url) =>
Task::run(fn() => file_get_contents($url))
);
$results = Promise::all($promises)->await();
// Async waterfall pattern (e.g., auth → fetch data → process)
$result = Task::run(fn() => auth()->user())
->then(fn($user) => Task::run(fn() => UserService::fetchProfile($user)))
->then(fn($profile) => Task::run(fn() => $profile->process()))
->catch(fn($e) => logger()->error($e))
->await();
// Retry mechanism with exponential backoff
$task = Task::run(fn() => externalApiCall());
$task->retry(3, fn($attempt) => $attempt * 100); // Retry 3x with delays
$result = $task->catch(fn($e) => fallbackData())->await();
// Offload async tasks to Laravel's queue system
Task::later(now()->addSeconds(10), fn() =>
dispatch(new ProcessPaymentJob($userId))
);
public function handle(Request $request, Closure $next) {
return Async::run(fn() => $next($request))->await();
}
// Defer heavy computations to async
User::find(1)->then(fn($user) =>
Task::run(fn() => $user->generateReport())
);
protected function handle() {
Async::parallel([
fn() => $this->processUsers(),
fn() => $this->generateStats(),
])->await();
}
// In PHPUnit tests
Async::shouldReceive('task')
->once()
->andReturn(new MockPromise([$expectedResult]));
$result = Async::task(fn() => ...)->await();
$this->assertEquals($expectedResult, $result);
// Custom test trait for async assertions
trait AsyncAssertions {
public function assertAwaits(Promise $promise, $expected) {
$result = $promise->await();
$this->assertEquals($expected, $result);
}
}
Blocking the Main Thread
await() in Laravel routes/middleware can block the request.Task::later() or offload to queues.
// Anti-pattern (blocks!)
$result = Task::run(fn() => heavyTask())->await();
// Pattern (non-blocking)
Task::later(now()->addSecond(), fn() => heavyTask());
Connection Leaks (Databases)
DB::connection()->getPdo() or wrap in a queue job.
// Risky: Async DB query
Task::run(fn() => DB::table('users')->get());
// Safer: Queue job
dispatch(new AsyncDbJob());
Uncaught Promise Rejections
.catch() or use a global handler.
Async::task($task)->catch(fn($e) => report($e));
Memory Bloat
Task::run()->detach() for fire-and-forget tasks.
Task::run(fn() => cleanupOldLogs())->detach();
Laravel’s Promise Facade Conflict
illuminate/support/Promise.Async\Promise explicitly or alias:
use Async\Promise as AsyncPromise;
Pause Async Execution
// Simulate async delays for debugging
Task::run(fn() => sleep(2))->await();
Inspect Pending Tasks
// Log all active tasks (if package supports it)
logger()->debug(Async::getActiveTasks());
Timeout Handling
// Add timeouts to tasks
$task = Task::run(fn() => slowOperation())
->timeout(5.0) // 5 seconds
->catch(fn($e) => throw new TimeoutException());
Custom Task Schedulers
Async\Scheduler to integrate with Laravel’s queue system.class LaravelQueueScheduler implements Scheduler {
public function schedule(Task $task, ?\DateTimeInterface $delay = null) {
dispatch(new AsyncTaskJob($task));
}
}
Async Event Listeners
// Listen to events asynchronously
event(new UserRegistered($user))
->then(fn() => Task::run(fn() => sendWelcomeEmail($user)));
Async Service Providers
public function boot() {
Async::task(fn() => $this->initializeCache())->detach();
}
Concurrency Limits
Async::setConcurrency(10); // Max 10 concurrent tasks
Error Reporting
Async::setErrorHandler(fn($e) => logger()->critical($e));
Fiber Compatibility
--enable-fiber-support if using custom runtimes.Service Container Binding
Async instance to Laravel’s container for dependency injection:
$this->app->singleton(Async::class, fn() => new Async());
Queue vs. Async
Promise::all).Async in Jobs
Testing Async Code
Async::fake() (if supported) or mock Task/Promise:
Async::shouldReceive('task')->andReturn(new MockPromise([$data]));
How can I help you explore Laravel packages today?