nunomaduro/pokio

Pokio is a dead simple asynchronous API for PHP that just works

dev-main 2025-06-04 19:12 UTC

This package is auto-updated.

Last update: 2025-06-04 19:12:47 UTC


README

Caution: This package is a work in progress and it manipulates process lifecycles using low-level and potentially unsafe techniques such as FFI for inter-process communication, and preserving state across process spawns. It is intended strictly for internal use (e.g., performance optimizations in Pest). Use at your own risk—no guarantees are provided.

Logo for pokio

Pokio

Build Status Total Downloads Latest Stable Version License

Pokio is a dead simple Asynchronous API for PHP that just works! Here is an example:

$promiseA = async(function () {
    sleep(2);
    
    return 'Task 1';
});

$promiseB = async(function () {
    sleep(2);
    
    return 'Task 2';
});

// just takes 2 seconds...
[$resA, $resB] = await([$promiseA, $promiseB]);

echo $resA; // Task 1
echo $resB; // Task 2

Installation

Requires PHP 8.3+.

⚡️ Get started by requiring the package using Composer:

composer require nunomaduro/pokio:@dev

Usage

  • async

The async global function returns a promise that will eventually resolve the value returned by the given closure.

$promise = async(function () {
    return 1 + 1;
});

var_dump(await($promise)); // int(2)

Similar to other promise libraries, Pokio allows you to chain methods to the promise (like then, catch, etc.).

The then method will be called when the promise resolves successfully. It accepts a closure that will receive the resolved value as its first argument.

$promise = async(fn (): int => 1 + 2)
    ->then(fn ($result): int => $result + 2)
    ->then(fn ($result): int => $result * 2);

$result = await($promise);
var_dump($result); // int(10)

Optionally, you may chain a catch method to the promise, which will be called if the given closure throws an exception.

$promise = async(function () {
    throw new Exception('Error');
})->catch(function (Throwable $e) {
    return 'Rescued: ' . $e->getMessage();
});

var_dump(await($promise)); // string(16) "Rescued: Error"

If you don't want to use the catch method, you can also use native try/catch block.

$promise = async(function () {
    throw new Exception('Error');
});

try {
    await($promise);
} catch (Throwable $e) {
    var_dump('Rescued: ' . $e->getMessage()); // string(16) "Rescued: Error"
}

Similar to the catch method, you may also chain a finally method to the promise, which will be called regardless of whether the promise resolves successfully or throws an exception.

$promise = async(function (): void {
    throw new RuntimeException('Exception 1');
})->finally(function () use (&$called): void {
    echo "Finally called\n";
});

If you return a promise from the closure, it will be awaited automatically.

$promise = async(function () {
    return async(function () {
        return 1 + 1;
    });
});

var_dump(await($promise)); // int(2)
  • await

The await global function will block the current process until the given promise resolves.

$promise = async(function () {
    sleep(2);
    
    return 1 + 1;
});

var_dump(await($promise)); // int(2)

You may also pass an array of promises to the await function, which will be awaited simultaneously.

$promiseA = async(function () {
    sleep(2);
    
    return 1 + 1;
});

$promiseB = async(function () {
    sleep(2);
    
    return 2 + 2;
});

var_dump(await([$promiseA, $promiseB])); // array(2) { [0]=> int(2) [1]=> int(4) }

Follow Nuno

License

Pokio was created by Nuno Maduro under the MIT license.