nunomaduro/pokio
Pokio is a simple async API for PHP: run closures concurrently via pcntl forks and await results. Uses FFI shared memory for fast parent/child communication. Built for internal tooling and performance work; not recommended for production use.
Installation:
composer require nunomaduro/pokio
Ensure your project uses PHP 8.3+ and has pcntl and ffi extensions enabled (though Pokio gracefully falls back to sequential execution if unavailable).
First Use Case:
Replace a blocking sleep() or sequential task with parallel execution:
$promise1 = async(function () {
// Simulate I/O-bound task (e.g., API call, DB query)
sleep(2);
return 'Task 1 result';
});
$promise2 = async(function () {
sleep(1);
return 'Task 2 result';
});
[$result1, $result2] = await([$promise1, $promise2]);
// Outputs: ["Task 1 result", "Task 2 result"] in ~2 seconds (not 3)
Key Entry Points:
async(Closure): Wrap a task to run asynchronously.await(Promise|array): Block and resolve one or multiple promises..then(), .catch(), .finally() for async workflows.Where to Look First:
foreach loops or sleep() delays with async/await.$urls = ['https://api.example.com/data1', 'https://api.example.com/data2'];
$promises = array_map(fn ($url) => async(fn () => file_get_contents($url)), $urls);
$results = await($promises);
array_map or collect()->map to batch tasks..catch() or use try/catch with await.$promise = async(fn () => 1/0)
->catch(fn (Throwable $e) => "Handled: {$e->getMessage()}");
echo await($promise); // "Handled: Division by zero"
array_map + await with a loop to catch individual errors..then() for sequential async operations.$promise = async(fn () => fetchData())
->then(fn ($data) => processData($data))
->then(fn ($result) => saveToDB($result));
await($promise);
.then() closures to auto-await nested async calls.v0.1.1).$promise = async(fn () => 42);
$result = $promise(); // Equivalent to await($promise)
use Illuminate\Console\Command;
use function Pokio\async, Pokio\await;
class ParallelProcessCommand extends Command
{
protected $signature = 'process:parallel';
public function handle()
{
$tasks = collect(range(1, 5))->map(fn ($i) =>
async(fn () => $this->processTask($i))
);
await($tasks);
}
}
pcntl or ffi are disabled.v1.0.1).if (!Pokio\supportsConcurrency()) {
// Handle sequential execution
}
$sharedData = async(fn () => [
'key' => 'value',
]);
$data = await($sharedData);
Testing:
async/await in unit tests by replacing with synchronous calls or using Pokio\supportsConcurrency() to conditionally test fallbacks.if (Pokio\supportsConcurrency()) {
$result = await(async(fn () => 'test'));
} else {
$result = 'test'; // Fallback
}
Debugging:
v1.0.1): No need to disable debugging for parallel tasks.XDEBUG_TRIGGER=1 in your environment to ensure Xdebug doesn’t interfere.Performance:
pcntl/ffi vs. fallback to validate gains.Artisan Commands:
Illuminate\Console\Command and use async/await in handle().this->info() to log progress during parallel execution.Pest Testing:
test('parallel tests', function () {
$promises = [
async(fn () => expect(true)->toBeTrue()),
async(fn () => expect(1+1)->toEqual(2)),
];
await($promises);
})->parallel();
No Production Support:
Xdebug Conflicts (Pre-v1.0.1):
v1.0.1, Pokio auto-disables forking in debug mode.>=v1.0.1 if debugging parallel tasks.Stateful Processes:
$repo = new UserRepository();
$promise = async(fn () use ($repo) => $repo->find(1));
Memory Limits:
ulimit -v).FFI/PCNTL Dependencies:
pcntl or ffi are missing, but this may not be desired.if (!Pokio\supportsConcurrency()) {
throw new RuntimeException('Async not supported in this environment.');
}
Error Propagation:
.catch() or try/catch with await:
try {
await(async(fn () => throw new Exception()));
} catch (Throwable $e) {
// Handle error
}
Shared Memory Leaks:
Check Concurrency Support:
var_dump(Pokio\supportsConcurrency()); // bool
false if pcntl/ffi are missing or Xdebug is active.Log Process IDs:
posix_getpid() in parent/child:
$
How can I help you explore Laravel packages today?