iterate

(PHP 8.6+, True Async 1.0.0)

iterate() — Concurrently iterates over an array or Traversable, calling a callback for each element.

Description

iterate(iterable $iterable, callable $callback, int $concurrency = 0, bool $cancelPending = true): void

Executes callback for each element of iterable in a separate coroutine. The concurrency parameter allows limiting the number of simultaneously running callbacks. The function blocks the current coroutine until all iterations complete.

All coroutines spawned via iterate() run in an isolated child Scope.

Parameters

iterable An array or an object implementing Traversable (including generators and ArrayIterator).

callback A function called for each element. Accepts two arguments: (mixed $value, mixed $key). If the callback returns false, iteration stops.

concurrency Maximum number of simultaneously running callbacks. Defaults to 0 — the default limit, all elements are processed concurrently. A value of 1 means execution in a single coroutine.

cancelPending Controls the behavior of child coroutines spawned inside the callback (via spawn()) after iteration completes.

Return Values

The function does not return a value.

Errors/Exceptions

Examples

Example #1 Basic array iteration

<?php
use function Async\spawn;
use function Async\iterate;

spawn(function() {
    $urls = [
        'php'    => 'https://php.net',
        'github' => 'https://github.com',
        'google' => 'https://google.com',
    ];

    iterate($urls, function(string $url, string $name) {
        $content = file_get_contents($url);
        echo "$name: " . strlen($content) . " bytes\n";
    });

    echo "All requests completed\n";
});
?>

Example #2 Limiting concurrency

<?php
use function Async\spawn;
use function Async\iterate;

spawn(function() {
    $userIds = range(1, 100);

    // Process no more than 10 users simultaneously
    iterate($userIds, function(int $userId) {
        $data = file_get_contents("https://api.example.com/users/$userId");
        echo "User $userId loaded\n";
    }, concurrency: 10);

    echo "All users processed\n";
});
?>

Example #3 Stopping iteration by condition

<?php
use function Async\spawn;
use function Async\iterate;

spawn(function() {
    $items = ['apple', 'banana', 'cherry', 'date', 'elderberry'];

    iterate($items, function(string $item) {
        echo "Processing: $item\n";

        if ($item === 'cherry') {
            return false; // Stop iteration
        }
    });

    echo "Iteration finished\n";
});
?>

Output:

Processing: apple
Processing: banana
Processing: cherry
Iteration finished

Example #4 Iterating over a generator

<?php
use function Async\spawn;
use function Async\iterate;

function generateTasks(): Generator {
    for ($i = 1; $i <= 5; $i++) {
        yield "task-$i" => $i;
    }
}

spawn(function() {
    iterate(generateTasks(), function(int $value, string $key) {
        echo "$key: processing value $value\n";
    }, concurrency: 2);

    echo "All tasks completed\n";
});
?>

Example #5 Cancelling spawned coroutines (cancelPending = true)

By default, coroutines spawned via spawn() inside the callback are cancelled after iteration completes:

<?php
use function Async\spawn;
use function Async\iterate;
use Async\AsyncCancellation;

spawn(function() {
    iterate([1, 2, 3], function(int $value) {
        // Spawn a background task
        spawn(function() use ($value) {
            try {
                echo "Background task $value started\n";
                suspend();
                suspend();
                echo "Background task $value finished\n"; // Won't execute
            } catch (AsyncCancellation) {
                echo "Background task $value cancelled\n";
            }
        });
    });

    echo "Iteration finished\n";
});
?>

Output:

Background task 1 started
Background task 2 started
Background task 3 started
Background task 1 cancelled
Background task 2 cancelled
Background task 3 cancelled
Iteration finished

Example #6 Waiting for spawned coroutines (cancelPending = false)

If you pass cancelPending: false, iterate() will wait for all spawned coroutines to complete:

<?php
use function Async\spawn;
use function Async\iterate;

spawn(function() {
    $results = [];

    iterate([1, 2, 3], function(int $value) use (&$results) {
        // Spawn a background task
        spawn(function() use (&$results, $value) {
            suspend();
            $results[] = "result-$value";
        });
    }, cancelPending: false);

    // All background tasks have completed
    sort($results);
    echo implode(', ', $results) . "\n";
});
?>

Output:

result-1, result-2, result-3

Example #7 Error handling

<?php
use function Async\spawn;
use function Async\iterate;

spawn(function() {
    try {
        iterate([1, 2, 3, 4, 5], function(int $value) {
            if ($value === 3) {
                throw new RuntimeException("Error processing element $value");
            }
            echo "Processed: $value\n";
        });
    } catch (RuntimeException $e) {
        echo "Caught: " . $e->getMessage() . "\n";
    }
});
?>

Notes

Note: iterate() creates an isolated child Scope for all spawned coroutines.

Note: When an array is passed, iterate() creates a copy of it before iteration. Modifying the original array inside the callback does not affect the iteration.

Note: If the callback returns false, iteration stops, but already running coroutines continue until completion (or cancellation, if cancelPending = true).

Changelog

Version Description
1.0.0 Added the iterate() function.

See Also