ReactPHP
Promise Handling
Asynchronous Programming
Concurrent Requests
PHP Development

waiting for all promises to finish in react PHP

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This topic is about ReactPHP, not the React JavaScript UI library. In ReactPHP, the usual way to wait for multiple asynchronous operations to complete is React\Promise\all(), which returns a promise that resolves when all input promises resolve.

Use React\Promise\all() for the Happy Path

If you have several async operations and you want their results only after all succeed, use all():

php
1<?php
2
3require 'vendor/autoload.php';
4
5use React\EventLoop\Loop;
6use React\Promise\Deferred;
7use function React\Promise\all;
8
9function delayedValue(string $value, float $seconds)
10{
11    $deferred = new Deferred();
12
13    Loop::addTimer($seconds, function () use ($deferred, $value) {
14        $deferred->resolve($value);
15    });
16
17    return $deferred->promise();
18}
19
20$promises = [
21    delayedValue('first', 0.1),
22    delayedValue('second', 0.2),
23    delayedValue('third', 0.3),
24];
25
26all($promises)->then(function (array $results) {
27    print_r($results);
28});
29
30Loop::run();

The resolved value is an array of results in the same order as the input promises, which makes it easy to correlate results with requests.

Understand the Failure Behavior

all() rejects as soon as one promise rejects. That is correct when the overall task should fail if any one subtask fails.

For example, if three required API calls must all succeed before you can continue, this behavior is exactly what you want.

However, if your real goal is "wait for everything, even failures, then inspect the outcomes," all() alone is not enough.

How to Wait for All Outcomes, Not Just All Successes

You can wrap each promise so it always resolves to a status object. Then all() can still wait for every operation without failing fast.

php
1<?php
2
3require 'vendor/autoload.php';
4
5use React\Promise\Deferred;
6use function React\Promise\all;
7
8function toSettled($promise)
9{
10    return $promise->then(
11        fn($value) => ['state' => 'fulfilled', 'value' => $value],
12        fn(Throwable $error) => ['state' => 'rejected', 'reason' => $error->getMessage()]
13    );
14}

Applied to a set of promises:

php
1$wrapped = array_map('toSettled', $promises);
2
3all($wrapped)->then(function (array $results) {
4    print_r($results);
5});

Now the overall wait completes after every promise settles, and you can inspect each result without the whole join failing on the first error.

A Real ReactPHP Pattern with HTTP Requests

This becomes useful with concurrent HTTP calls:

php
1<?php
2
3require 'vendor/autoload.php';
4
5use React\Http\Browser;
6use React\EventLoop\Loop;
7use function React\Promise\all;
8
9$browser = new Browser();
10
11$promises = [
12    $browser->get('https://example.com/a'),
13    $browser->get('https://example.com/b'),
14];
15
16all($promises)->then(
17    function (array $responses) {
18        foreach ($responses as $response) {
19            echo $response->getStatusCode() . PHP_EOL;
20        }
21    },
22    function (Throwable $error) {
23        echo 'Request batch failed: ' . $error->getMessage() . PHP_EOL;
24    }
25);
26
27Loop::run();

The important part is that the code remains non-blocking. You are not sleeping or polling. You are composing promises and letting the event loop continue driving work.

Keep Promise Composition Clear

When people struggle with "waiting" in ReactPHP, the real issue is often that they are still thinking in blocking control flow. In ReactPHP, the goal is usually not to pause the thread until the work finishes. The goal is to express what should happen once the work finishes.

That means you should prefer:

  • promise composition
  • 'then, catch, and finally'
  • small join points with all()

over attempts to turn async code back into synchronous code manually.

Common Pitfalls

The biggest pitfall is expecting all() to wait for both successes and failures by default. It waits for all successes, but rejects early on the first failure.

Another mistake is forgetting to keep the event loop running. If the loop stops, your promises will never resolve.

Developers also sometimes assume the returned results are ordered by completion time. They are not. They are ordered by the input array, which is usually the behavior you want.

Finally, do not mix blocking PHP functions into ReactPHP flows unless you know exactly why. Blocking work defeats the point of the event loop.

Summary

  • In ReactPHP, use React\Promise\all() to join multiple promises.
  • 'all() resolves with ordered results when every promise succeeds.'
  • 'all() rejects early if any promise rejects.'
  • Wrap promises into status objects when you need every outcome, not just the happy path.
  • Keep the event loop running and avoid blocking calls inside async flows.

Course illustration
Course illustration

All Rights Reserved.