PHP code example of hiblaphp / async

1. Go to this page and download the library: Download hiblaphp/async library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

hiblaphp / async example snippets


use function Hibla\async;
use function Hibla\await;
use function Hibla\sleep;

// Fetch three resources concurrently. Each async() runs in its own Fiber.
[$user, $orders, $stats] = await(Promise::all([
    async(fn() => fetchUser(1)),
    async(fn() => fetchOrders(1)),
    async(fn() => fetchStats(1)),
]));

// Write sequential async logic that reads like synchronous code
$report = await(async(function () {
    $user   = await(fetchUser(1));
    $orders = await(fetchOrders($user->id));

    sleep(0.5); // Suspends this Fiber while other work continues in the background.

    return generateReport($user, $orders);
}));

// Outside a Fiber, await() holds the script here while the event loop
// keeps running underneath. Timers fire, and other in-flight work continues.
$user = await(fetchUser(1));
echo $user->name;

use function Hibla\async;
use function Hibla\await;

// A plain function with no special marking and no color
function getUser(int $id): User
{
    return await(fetchUser($id));
}

// Works synchronously at the top level without needing async()
$user = getUser(1);

// Works concurrently when wrapped in async() without any changes to getUser()
$promise = async(fn() => getUser(1));

function fetchUserProfile(int $id): PromiseInterface
{
    return async(function () use ($id) {
        $user   = await(Http::get("/users/$id"));
        $avatar = await(Http::get("/avatars/$id"));

        return ['user' => $user, 'avatar' => $avatar];
    });
}

async(function () {
    // Multiple profiles load concurrently because each async() call
    // runs in its own Fiber and suspends independently at each await()
    [$page1, $page2, $page3] = await(Promise::all([
        fetchUserProfile(1),
        fetchUserProfile(2),
        fetchUserProfile(3),
    ]));
});

use function Hibla\async;

$promise = async(function () {
    return 'hello from a fiber';
});

$promise->then(fn($value) => print($value)); // hello from a fiber

$start = microtime(true);

async(function () {
    await(delay(1));
    echo "Task 1 done\n";
});

async(function () {
    await(delay(1));
    echo "Task 2 done\n";
});

async(function () {
    await(delay(1));
    echo "Task 3 done\n";
});

// All three run concurrently. The total time is ~1 second, not 3.
echo microtime(true) - $start; // ~1.0

// Wrong: blocks the entire loop for 2 seconds
async(function () {
    \sleep(2); // PHP's native sleep function stalls everything
    echo "done\n";
});

// Correct: suspends this Fiber cooperatively so the loop stays free
async(function () {
    sleep(2); // Hibla's sleep function. Use function Hibla\sleep
    echo "done\n";
});

$promise = async(function () {
    throw new \RuntimeException('Something went wrong');
});

$promise->catch(fn($e) => print($e->getMessage())); // Something went wrong

async(function () {
    try {
        $result = await(riskyOperation());
        return $result;
    } catch (\Throwable $e) {
        logError($e);
        return null;
    }
});

// Wrong: allocates a full Fiber just to await one existing promise
$result = await(async(fn() => await(Http::get('/api/data'))));

// Correct: await the promise directly without needing a Fiber
$result = await(Http::get('/api/data'));

function getUserName(int $id): string
{
    $user = await(fetchUser($id));
    return $user->name;
}

// Wrong: getUserName() already works in both contexts
$name = await(async(fn() => getUserName(1)));

// Correct: call it directly
$name = getUserName(1);

// Only wrap in async() when you specifically want concurrent execution
$promise = async(fn() => getUserName(1)); // justified due to explicit concurrency

// Good use: multiple awaits with logic between them
$promise = async(function () {
    $user    = await(fetchUser(1));
    $orders  = await(fetchOrders($user->id));
    $ratings = await(fetchRatings($user->id));

    return processData($user, $orders, $ratings);
});

use function Hibla\await;

$user = await(fetchUser(1));
echo $user->name;

// Outside a Fiber: holds the script here while the event loop keeps running underneath
$user = await(fetchUser(1));

// Inside a Fiber: suspends cooperatively
async(function () {
    $user = await(fetchUser(1)); // other work runs while waiting
    echo $user->name;
});

async(function () {
    try {
        $user = await(fetchUser(999)); // rejects with NotFoundException
    } catch (\NotFoundException $e) {
        echo "User not found\n";
    }
});

async(function () use ($token) {
    try {
        $user = await(fetchUser(1), $token);
    } catch (\Hibla\Promise\Exceptions\CancelledException $e) {
        echo "Fetch was cancelled\n";
    }
});

use Hibla\Cancellation\CancellationTokenSource;
use function Hibla\async;
use function Hibla\await;

$cts = new CancellationTokenSource(5.0); // 5 second timeout

async(function () use ($cts) {
    try {
        $user   = await(fetchUser(1), $cts->token);
        $orders = await(fetchOrders($user->id), $cts->token);

        return compact('user', 'orders');
    } catch (\Hibla\Promise\Exceptions\CancelledException $e) {
        echo "Operation timed out or was cancelled\n";
    }
});

// Plain functions using await() internally n await(Http::get("/users/$id")->then(
        fn($r) => User::fromArray(json_decode($r->getBody(), true))
    ));
}

function getUserWithOrders(int $id): array
{
    $user   = getUser($id);
    $orders = await(fetchOrders($user->id));

    return compact('user', 'orders');
}

// 1. Synchronous: holds the script at each call while the event loop keeps running underneath
$data = getUserWithOrders(1);

// 2. Single async task: runs in a Fiber and suspends cooperatively
$promise = async(fn() => getUserWithOrders(1));

// 3. Concurrent: multiple users fetched concurrently
$promises = array_map(
    fn($id) => async(fn() => getUserWithOrders($id)),
    [1, 2, 3, 4, 5]
);
await(Promise::all($promises));

// 4. With concurrency limiting
await(Promise::concurrent(
    array_map(
        fn($id) => fn() => async(fn() => getUserWithOrders($id)),
        range(1, 100)
    ),
    concurrency: 10
));

use function Hibla\asyncFn;

function processRecord(array $record): array
{
    $enriched  = await(enrichRecord($record));
    $validated = await(validateRecord($enriched));

    return $validated;
}

// Create an async version without changing processRecord()
$asyncProcess = asyncFn('processRecord');

// Primary use case: passing to Promise::map() or Promise::concurrent()
await(Promise::map($records, $asyncProcess, concurrency: 10));

use function Hibla\sleep;

async(function () {
    echo "Task 1 start\n";
    sleep(2);
    echo "Task 1 done\n";
});

async(function () {
    echo "Task 2 start\n";
    sleep(1);
    echo "Task 2 done\n"; // runs before Task 1
});

// Output:
// Task 1 start
// Task 2 start
// Task 2 done  (~1 second)
// Task 1 done  (~2 seconds)
// Total time: ~2 seconds, not 3

> use function Hibla\sleep; // sleep(1);  // Hibla's sleep — correct
>     \sleep(1); // PHP's native sleep — stalls the entire loop
> });
> 

use function Hibla\inFiber;

function getStatus(): string
{
    if (inFiber()) {
        return await(fetchStatusAsync());
    }

    return fetchStatusSync();
}

use Hibla\Async\AsyncEnvironment;

// Activating strict validation (Highly recommended for high-concurrency daemons)
AsyncEnvironment::enableStrictAwait();

// Restoring the cooperative blocking fallback (The default setting)
AsyncEnvironment::disableStrictAwait();

use Hibla\Cancellation\CancellationTokenSource;
use function Hibla\async;
use function Hibla\await;

$cts = new CancellationTokenSource();

$workflow = async(function () use ($cts) {
    $connection = openConnection();

    try {
        $user   = await(fetchUser(1), $cts->token);
        $orders = await(fetchOrders($user->id), $cts->token);
        $report = await(generateReport($user, $orders), $cts->token);

        return $report;
    } catch (\Hibla\Promise\Exceptions\CancelledException $e) {
        echo "Workflow cancelled\n";
        return null;
    } finally {
        // Always runs on normal completion, exception, or cancellation
        $connection->close();
    }
});

// Cancel from anywhere, causing the next await() in the workflow to throw
Loop::addTimer(2.0, fn() => $cts->cancel());

$result = await($workflow);

$cts = new CancellationTokenSource(5.0);

$workflow = async(function () use ($cts) {
    // Http::get() has an onCancel() handler that aborts the curl request.
    // Passing $cts->token to await() is enough, so no manual track() call is needed.
    $response = await(Http::get('https://api.example.com/users'), $cts->token);
    $data     = await(Http::get('https://api.example.com/orders'), $cts->token);

    return compact('response', 'data');
});

[$users, $products, $stats] = await(Promise::all([
    async(fn() => fetchUsers()),
    async(fn() => fetchProducts()),
    async(fn() => fetchStats()),
]));

$results = await(Promise::concurrent(
    array_map(
        fn($id) => fn() => async(function () use ($id) {
            $user   = await(fetchUser($id));
            $orders = await(fetchOrders($user->id));

            return compact('user', 'orders');
        }),
        range(1, 100)
    ),
    concurrency: 10
));

$fastest = await(Promise::race([
    async(fn() => fetchFromRegionA()),
    async(fn() => fetchFromRegionB()),
    async(fn() => fetchFromRegionC()),
]));

$cts = new CancellationTokenSource();

try {
    $result = await(Promise::timeout(
        async(function () use ($cts) {
            return await(slowOperation(), $cts->token);
        }),
        seconds: 5.0
    ));
} catch (\Hibla\Promise\Exceptions\TimeoutException $e) {
    echo "Operation timed out\n";
}

public function test_fetch_user(): void
{
    $user = await(fetchUser(1));

    $this->assertEquals('John', $user->name);
}

public function test_concurrent_fetch(): void
{
    [$user, $orders] = await(Promise::all([
        fetchUser(1),
        fetchOrders(1),
    ]));

    $this->assertNotEmpty($orders);
}

public function test_cancellation(): void
{
    $cts = new CancellationTokenSource();
    $cts->cancel();

    $this->expectException(\Hibla\Promise\Exceptions\CancelledException::class);

    await(fetchUser(1), $cts->token);
}