docs/promise-quick-start.md
This guide covers the common promise operations needed when using
guzzlehttp/promises directly: registering callbacks, resolving or rejecting
promises, waiting synchronously, composing chains, and using generator-based
flows. For the full public surface, see the Promise API.
A promise represents the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its then method, which
registers callbacks to receive either the eventual value or the reason why the
promise cannot be fulfilled.
Guzzle promise callbacks are queued. They run when the task queue is drained,
for example by Utils::queue()->run(), by waiting on a returned promise, or by
the default shutdown handler at the end of the PHP process. Examples that show
callback output drain the queue explicitly.
Callbacks are registered with the then method by providing an optional
$onFulfilled followed by an optional $onRejected function.
use GuzzleHttp\Promise\Promise;
$promise = new Promise();
$promise->then(
// $onFulfilled
function ($value) {
echo 'The promise was fulfilled.';
},
// $onRejected
function ($reason) {
echo 'The promise was rejected.';
}
);
Resolving a promise means that you either fulfill a promise with a value or
reject a promise with a reason. Callbacks registered with then are invoked
only once and in the order in which they were added when the queue is drained.
Promises are fulfilled using the resolve($value = null) method. Calling
resolve() without an argument fulfills the promise with null. Resolving a
promise with any value other than a GuzzleHttp\Promise\RejectedPromise queues
the $onFulfilled callbacks. Resolving with a rejected promise rejects the
promise and queues the $onRejected callbacks.
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\Utils;
$promise = new Promise();
$promise
->then(function ($value) {
return 'Hello, ' . $value;
})
->then(function ($value) {
echo $value;
});
$promise->resolve('reader.');
Utils::queue()->run();
// Outputs "Hello, reader."
Promises can be chained one after the other. Each then call returns a new
promise. The return value of a callback is forwarded to the next promise in the
chain. Returning a promise from a callback makes the next promise wait for that
returned promise to settle.
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\Utils;
$promise = new Promise();
$nextPromise = new Promise();
$promise
->then(function ($value) use ($nextPromise) {
echo $value;
return $nextPromise;
})
->then(function ($value) {
echo $value;
});
$promise->resolve('A');
Utils::queue()->run();
// Outputs "A"
$nextPromise->resolve('B');
Utils::queue()->run();
// Outputs "B"
When a promise is rejected, the $onRejected callbacks are invoked with the
rejection reason when the queue is drained.
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\Utils;
$promise = new Promise();
$promise->then(null, function ($reason) {
echo $reason;
});
$promise->reject('Error!');
Utils::queue()->run();
// Outputs "Error!"
If an exception is thrown in an $onRejected callback, subsequent
$onRejected callbacks receive the thrown exception as the reason.
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\Utils;
$promise = new Promise();
$promise->then(null, function ($reason) {
throw new Exception($reason);
})->then(null, function ($reason) {
assert($reason->getMessage() === 'Error!');
});
$promise->reject('Error!');
Utils::queue()->run();
You can also forward a rejection down the promise chain by returning a
GuzzleHttp\Promise\RejectedPromise in either an $onFulfilled or
$onRejected callback.
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Promise\Utils;
$promise = new Promise();
$promise->then(null, function ($reason) {
return new RejectedPromise($reason);
})->then(null, function ($reason) {
assert($reason === 'Error!');
});
$promise->reject('Error!');
Utils::queue()->run();
If an exception is not thrown in an $onRejected callback and the callback
does not return a rejected promise, downstream $onFulfilled callbacks are
invoked using the value returned from the $onRejected callback.
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\Utils;
$promise = new Promise();
$promise
->then(null, function ($reason) {
return "It's ok";
})
->then(function ($value) {
assert($value === "It's ok");
});
$promise->reject('Error!');
Utils::queue()->run();
You can synchronously force promises to complete using a promise's wait
method. When creating a promise, you can provide a wait function that is used
to synchronously complete the promise. The wait function receives a boolean
argument and is expected to resolve or reject the promise. If the wait function
does not settle the promise, an exception is thrown.
use GuzzleHttp\Promise\Promise;
$promise = new Promise(function (bool $recursive) use (&$promise) {
$promise->resolve('foo');
});
echo $promise->wait();
// Outputs "foo"
If a throwable is encountered while invoking the wait function of a promise, the promise is rejected with the throwable and the throwable is thrown.
$promise = new Promise(function (bool $recursive) use (&$promise) {
throw new Exception('foo');
});
$promise->wait(); // Throws the exception.
Calling wait on a promise that has been fulfilled will not trigger the wait
function. It will simply return the previously resolved value.
$promise = new Promise(function (bool $recursive) { die('this is not called!'); });
$promise->resolve('foo');
echo $promise->wait();
// Outputs "foo"
Calling wait on a promise that has been rejected will throw. If the rejection
reason is an instance of \Throwable, the reason is thrown. Otherwise, a
GuzzleHttp\Promise\RejectionException is thrown and the reason can be
obtained by calling getReason() on the exception.
$promise = new Promise();
$promise->reject('foo');
$promise->wait();
PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with reason: foo'
When synchronously waiting on a promise, you are joining the state of the promise into the current execution: the fulfilled value is returned, or the rejection reason is thrown. This is called "unwrapping" the promise. Waiting on a promise unwraps by default.
You can force a promise to resolve and not unwrap its state by passing
false to wait():
$promise = new Promise();
$promise->reject('foo');
// This does not throw. It only ensures the promise has been resolved.
$promise->wait(false);
When unwrapping a promise, the resolved value of the promise will be waited on until the unwrapped value is not a promise. This means that if promise A is resolved with promise B, unwrapping promise A returns the value delivered to promise B.
When you do not unwrap the promise, no value is returned.
Utils::inspect($promise) waits for a promise to settle and returns an array
describing its final state. For rejected promises, the reason entry is the
actual rejection reason delivered to rejection callbacks.
This means RejectionException and subclasses are not unwrapped by
inspect(). For example, cancelled promises inspect with a
CancellationException reason.
Coroutine::of() creates a promise from a generator that yields values or
promises. The generator resumes each time the yielded value settles, which can
make sequential asynchronous flows easier to read.
use GuzzleHttp\Promise\Coroutine;
use GuzzleHttp\Promise\FulfilledPromise;
$promise = Coroutine::of(function () {
$first = yield new FulfilledPromise('A');
$second = yield new FulfilledPromise($first . 'B');
yield $second . 'C';
});
echo $promise->wait();
// Outputs "ABC"
If a yielded promise rejects, the rejection is thrown into the generator. Catch it inside the generator to recover, or let it reject the coroutine promise.
You can cancel a promise that has not yet been fulfilled using cancel(). When
creating a promise, you can provide an optional cancel function that cancels the
underlying operation, such as closing a socket or aborting a query.
use GuzzleHttp\Promise\Promise;
$promise = new Promise(null, function () {
// Cancel the underlying operation.
});
$promise->cancel();
Cancellation rejects the promise with a CancellationException unless the
cancel function settles the promise first.