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 configuration required—just autoload.
First Use Case: Parallel HTTP Requests
use Async\Task;
use Async\Promise;
// Run multiple HTTP calls concurrently
$promises = [
Task::run(fn() => file_get_contents('https://api.example.com/data1')),
Task::run(fn() => file_get_contents('https://api.example.com/data2')),
];
// Await all results
$results = Promise::all($promises);
print_r($results);
Where to Look First
Task class: Core primitive for wrapping async operations.Promise class: For composing and awaiting results.Async facade (if provided): Convenience methods for common patterns.examples/ directory (if included in the package): Real-world snippets.Fan-Out/Fan-In Pattern
$tasks = collect($urls)->map(fn($url) => Task::run(fn() => http_get($url)));
$results = Promise::all($tasks); // Waits for all to complete
Error Handling with Promise::any()
$promises = [
Task::run(fn() => riskyOperation1()),
Task::run(fn() => riskyOperation2()),
];
$firstSuccess = Promise::any($promises); // Resolves on first success
Chaining Async Operations
$result = Task::run(fn() => fetchData())
->then(fn($data) => processData($data))
->then(fn($processed) => saveToDb($processed));
Integration with Laravel Queues
// Dispatch a job asynchronously
Task::run(fn() => dispatch(new ProcessPaymentJob($order)))
->then(fn() => logCompletion($order));
Task::run() to wrap dispatch() calls for async job execution.
Task::run(fn() => dispatch(new LongRunningJob($data)));
Async\EventLoop (if supported).Task::run() outside transactions or implement retry logic.Blocking the Event Loop:
Avoid long-running synchronous code inside Task::run(). Use Task::delay() for timeouts or split work into smaller chunks.
// ❌ Bad: Blocks the event loop
Task::run(fn() => sleep(10));
// ✅ Good: Use delays or async alternatives
Task::delay(1000, fn() => doWork());
Error Propagation:
Unhandled exceptions in Task::run() will propagate to the Promise unless caught explicitly.
// ❌ Silent failure
Task::run(fn() => throw new RuntimeException());
// ✅ Explicit error handling
Task::run(fn() => riskyOperation())
->catch(fn(\Throwable $e) => logError($e));
Stateful Closures:
Closures passed to Task::run() may capture variables by reference, leading to race conditions.
// ❌ Unpredictable state
$counter = 0;
Task::run(fn() => $counter++);
// ✅ Use immutable data or locks
Task::run(fn() => incrementCounter($counter));
Task::run()->then(fn() => logger()->debug('Task completed')) to trace async flows.Task::run()->timeout(5000) (5 seconds) to avoid hanging.Promise::all() calls without proper error handling—use Promise::allSettled() for robustness.Custom Task Schedulers:
Extend Async\Scheduler to integrate with custom queues (e.g., Redis, RabbitMQ).
class CustomScheduler extends Scheduler {
public function run(Closure $task) { /* ... */ }
}
Middleware for Tasks: Add pre/post-processing to tasks via decorators.
$task = new Task(fn() => doWork());
$task->pipe(new LoggingMiddleware());
Promise Compositors:
Create custom combinators (e.g., Promise::race(), Promise::reduce()) by extending Promise.
How can I help you explore Laravel packages today?