Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Async Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require php-standard-library/async
    

    No additional configuration is needed beyond autoloading.

  2. First Use Case: Async Task Execution in Laravel Replace a synchronous loop with parallel async tasks in a Laravel job or controller:

    use Async\Task;
    use Async\Promise;
    
    public function processMultipleUsers(array $userIds) {
        $tasks = collect($userIds)->map(fn($id) =>
            Task::run(fn() => $this->processUser($id))
        );
    
        return Promise::all($tasks)->await();
    }
    
    private function processUser(int $id) {
        // Simulate I/O-bound work (e.g., API call, DB query)
        return User::find($id)->load('orders');
    }
    
  3. Where to Look First

    • Task class: The entry point for wrapping synchronous code as async tasks.
      • Methods: Task::run(), Task::later() (for delayed execution).
    • Promise class: For composing and awaiting results.
      • Methods: Promise::all(), Promise::race(), Promise::then(), Promise::catch().
    • Async facade: Laravel-friendly wrapper (if provided in future updates).
      • Example: Async::task(fn() => ...)->await().

Implementation Patterns

Core Workflows

1. Parallel Execution (Fan-Out)

Replace nested loops or sequential calls with parallel tasks:

// Sequential (blocking)
$user1 = $this->fetchUser(1);
$user2 = $this->fetchUser(2);
$user3 = $this->fetchUser(3);

// Parallel (non-blocking)
$promises = [
    Task::run(fn() => $this->fetchUser(1)),
    Task::run(fn() => $this->fetchUser(2)),
    Task::run(fn() => $this->fetchUser(3)),
];
$users = Promise::all($promises)->await();

2. Sequential Chaining (Async Pipelines)

Chain async operations like middleware:

$result = Task::run(fn() => $this->step1())
    ->then(fn($data) => $this->step2($data))
    ->then(fn($data) => $this->step3($data))
    ->await();

3. Error Handling

Use catch() to handle failures gracefully:

Task::run(fn() => $this->fetchData())
    ->catch(fn(Throwable $e) => logger()->error("Fetch failed: {$e->getMessage()}"))
    ->await();

4. Integration with Laravel Queues

Offload async tasks to queues for true background processing:

// Async task dispatched to queue
Task::later(now()->addSeconds(10), fn() => $this->sendEmail($user))
    ->dispatch(); // Uses Laravel's queue system

5. Hybrid Sync/Async Code

Mix async and sync code in controllers/jobs:

public function handle(Request $request) {
    // Sync: Validate request
    $data = $request->validate([...]);

    // Async: Process data in background
    Task::run(fn() => $this->processData($data))
        ->detach(); // Fire-and-forget

    return response()->json(['status' => 'processing']);
}

Laravel-Specific Patterns

Async Middleware

Use for non-blocking request processing (Laravel 11+ async routes):

public function handle(Request $request, Closure $next) {
    return Async::run(fn() => $next($request))->await();
}

Async Eloquent Queries

Warning: Avoid direct async DB queries in Laravel (connection leaks). Instead, use queues or batch processing:

// Anti-pattern (risky)
$users = Task::run(fn() => User::all())->await();

// Recommended
Task::run(fn() => User::chunk(100, fn($users) => $this->processChunk($users)))
    ->await();

Async Events

Dispatch async event listeners:

event(new UserRegistered($user))
    ->then(fn() => Task::run(fn() => $this->sendWelcomeEmail($user)))
    ->catch(fn($e) => logger()->error($e));

Gotchas and Tips

Pitfalls

1. Blocking the Event Loop

  • Issue: Async tasks must not block the main thread (e.g., sleep(), sync DB calls).
  • Fix: Use Task::later() for delays or offload to queues.
    // Bad: Blocks the event loop
    Task::run(fn() => sleep(5));
    
    // Good: Non-blocking delay
    Task::later(now()->addSeconds(5), fn() => $this->doWork());
    

2. Uncaught Promise Rejections

  • Issue: Unhandled errors in async tasks may crash silently.
  • Fix: Always use catch() or wrap in a try-catch:
    Task::run(fn() => $this->riskyOperation())
        ->catch(fn($e) => report($e));
    

3. Memory Leaks

  • Issue: Unawaited Task objects consume memory until garbage collected.
  • Fix: Explicitly await() or detach() tasks:
    // Leak: Task runs but result is ignored
    Task::run(fn() => $this->doWork());
    
    // Fixed: Explicitly await or detach
    Task::run(fn() => $this->doWork())->await();
    // OR
    Task::run(fn() => $this->doWork())->detach();
    

4. Laravel-Specific Quirks

  • Issue: Async code in route handlers may cause timeouts or deadlocks.
  • Fix: Use async middleware only in Laravel 11+ async routes.
    // Laravel 11+ async route
    Route::get('/async', fn() => Async::run(fn() => $this->handleRequest()));
    

5. Database Connection Pooling

  • Issue: Async DB queries can exhaust Laravel’s connection pool.
  • Fix: Use raw PDO or queue jobs for DB-heavy async tasks:
    // Risky: Async DB query
    Task::run(fn() => DB::table('users')->get());
    
    // Safer: Queue job
    ProcessUsersJob::dispatch();
    

Debugging Tips

1. Logging Async Stack Traces

Use Async::debug() (if available) or manually log task IDs:

$task = Task::run(fn() => $this->doWork());
logger()->debug("Task ID: {$task->getId()}");

2. Testing Async Code

Mock Task and Promise in PHPUnit:

$mockTask = Mockery::mock(Task::class);
$mockTask->shouldReceive('await')->andReturn($expectedResult);

$this->app->instance(Task::class, $mockTask);

3. Timeout Handling

Add timeouts to prevent hanging:

Task::run(fn() => $this->slowOperation())
    ->timeout(10) // 10 seconds
    ->catch(TimeoutException::class, fn() => logger()->warning("Task timed out"));

Extension Points

1. Custom Task Schedulers

Extend Task to support cron-like scheduling:

class ScheduledTask extends Task {
    public static function cron(string $expression, callable $callback) {
        // Integrate with Laravel's scheduler or a custom cron parser
    }
}

2. Async Retry Logic

Implement exponential backoff for resilient tasks:

function retryAsync(callable $task, int $maxAttempts = 3) {
    return Task::run(fn() => $task())
        ->catch(fn($e) => $maxAttempts > 0
            ? retryAsync($task, $maxAttempts - 1)
            : throw $e);
}

3. Laravel Service Provider Integration

Bind the package to Laravel’s container:

public function register() {
    $this->app->singleton(Async::class, fn() => new Async());
    $this->app->alias(Async::class, 'async');
}

4. Async Job Wrapper

Create a Laravel job that wraps async tasks:

class AsyncJob implements ShouldQueue {
    use Dispatchable, InteractsWithQueue;

    public function handle() {
        Task::run(fn() => $this->asyncLogic())->await();
    }
}

Configuration Quirks

1. Concurrency Limits

Set global concurrency limits to avoid overwhelming the system:

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata