Weave 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

Laravel Async Laravel Package

vxm/laravel-async

vxm/laravel-async brings asynchronous and parallel execution to Laravel. Run closures and tasks concurrently with a simple API, await results, handle errors, and cap concurrency—ideal for speeding up slow I/O, reports, and batch jobs without restructuring your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and let Laravel discover the service provider:

composer require vxm/laravel-async   # ^5.0 for Laravel 11; ^4.x for Laravel 10 and below
php artisan vendor:publish --provider="VXM\Async\AsyncServiceProvider"   # publishes config/async.php

Laravel 11 requires v5.0.0 or later. If you are on Laravel 11 and Composer resolves to an older line (or refuses to resolve at all), that is the version matrix talking, not a broken package — check the constraint in composer.json before debugging anything else.

Then look at, in order: the package README (the authoritative API surface for your installed version), config/async.php (the only real config knob set — typically the PHP binary path used to spawn child processes), and src/Async.php / the Async facade, which is the entry point you'll actually call.

The smallest useful thing is fire-and-forget from a controller:

use VXM\Async\Facades\Async;

public function store(Request $request)
{
    $order = Order::create($request->validated());

    Async::run(function () use ($order) {
        Log::info('order created', ['id' => $order->id]);
        // e.g. hit a webhook, warm a cache, send an internal notification
    });

    return response()->json($order, 201);
}

What you get is a separate PHP process that boots the Laravel application, unserializes the closure, and runs it — so the user's request returns immediately while the work happens elsewhere. It is not a queue: there is no retry, no persistence, no supervisor. Treat it as "parallel/side process execution within the lifetime of a request" and it will make sense.

Implementation Patterns

Fan-out for latency. The strongest day-to-day use case is collapsing several sequential slow calls into parallel ones:

$ids = [1, 2, 3, 4];

foreach ($ids as $id) {
    Async::run(function () use ($id) {
        Cache::put("stats:$id", $this->api->stats($id), 300);
    });
}

Async::wait(); // block until all child processes finish
// read results back out of cache/db

The pattern is: spawn N tasks → wait → collect from a shared store (Redis, cache, database). The parent process never receives a return value, so the shared store is your results channel, not a return type.

Capture IDs, not models. Because the closure is serialized and shipped to a fresh process, pass scalars ($order->id, an array of settings) and re-fetch the model inside the child. Serializing an Eloquent model mostly works but drags connection/state baggage and any touched relations with it.

Wrap slow third-party integrations. PDF rendering, image resizing, report exports, mail relay, ERP/SMS/CRM sync calls. Anything where the caller does not need the answer in-band.

Use inside queue jobs, not instead of them. The reliable pattern for production: a real queued job owns durability, retries, and failure alerting; inside that job, use Async::run to parallelize the ten API calls it needs to make. Do not replace ShouldQueue with Async::run for anything that must eventually happen.

Guard against runaway spawning. Child processes each boot a full Laravel app. Batch large loops: chunk a 500-item list into 20 chunks of 25 and process chunks sequentially, rather than firing 500 processes at once.

Testing. Assert on the side effect (row in the table, cache key written, log emitted), not on the async call itself. Where the package offers a fake/sync mode, use it in feature tests; otherwise keep async calls behind a thin application service you can mock.

Gotchas and Tips

  • Everything captured must serialize. No PDO handles, resources, open streams, or closures over non-serializable state. If you get a serialization exception, the fix is almost always "capture an ID instead of the object."
  • No shared memory. The child process is a fresh application boot. auth()->user(), session(), request(), app() singletons you mutated, and in-memory static caches are all gone. Pass a user ID and call Auth::loginUsingId() (or better, Auth::loginUsingId($id, false)) inside the child if you need an actor.
  • Uncommitted DB writes are invisible. Never spawn an async task inside a DB::transaction() and expect the child to see the row. Commit first, then dispatch — or use DB::afterCommit() / queue jobs with $afterCommit.
  • Exceptions disappear. A throw in the child process does not propagate to the parent. Wrap the closure body in try/catch with Log::error(...), or you will silently lose failures for days.
  • Configure the PHP binary explicitly in deployed environments. config('async.php_binary') (or the php_binary key of the published config) must point at a real CLI binary. In containers, Forge, Vapor, and Herd, php is frequently not on PATH for the FPM user; use PHP_BINARY or an absolute path. This is the #1 "it works locally, does nothing in production" cause.
  • Watch timeouts and memory. Long children can outlive max_execution_time configured for CLI, or get OOM-killed without writing anything. Keep tasks short (seconds, not minutes) and log duration.
  • Don't await in a loop. Async::wait() per iteration serializes everything and gives you the worst of both worlds — accumulate, then wait once.
  • Windows/CLI semantics. Symfony Process-based spawning behaves slightly differently on Windows (no true fork); test there if you ship to Windows dev boxes, and don't rely on signals.
  • Extension point. The facade resolves a concrete runner class from the container, so you can bind your own implementation (e.g. one that routes to a queue in tests, or fakes the spawn) via a service provider — far cleaner than sprinkling if (app()->runningUnitTests()) through your code.
  • Version check and upgrade path. v5.0.0 is a major bump whose only change is Laravel 11 framework support — no new API, no deprecations, so existing run/wait call sites should carry over unchanged. Treat it as a constraint change: bump the requirement, composer update vxm/laravel-async, run php artisan config:clear if you re-publish config, and re-run your feature tests. Because a major bump usually signals a dropped minimum framework version, check the version matrix before upgrading a Laravel 10-or-below app; and pin the major version in composer.json since the API surface is minimal and mostly stable. Older releases (pre-5) differ in exact run/wait naming — read the README for the version you actually installed.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels