guzzlehttp/promises
Promises/A+ implementation for PHP with iterative, stack-safe chaining. Provides synchronous wait(), cancellation, interop with any thenable, and coroutine-style async/await via Coroutine::of(). Includes Promise, FulfilledPromise, and RejectedPromise.
Installation:
composer require guzzlehttp/promises
Ensure your composer.json targets PHP 7.2.5+ (for v2.x) or 5.5+ (for v1.x).
First Use Case: Replace callback hell in async operations (e.g., HTTP requests, DB queries) with promises. Example: Wrap a slow function in a promise:
use GuzzleHttp\Promise\Promise;
$promise = new Promise(function () {
// Simulate async work (e.g., API call)
sleep(1);
return "Result";
});
$promise->then(function ($result) {
echo "Async result: " . $result; // Outputs after 1 second
});
Where to Look First:
Promise, FulfilledPromise, and RejectedPromise.Use then() to chain dependent async tasks (e.g., sequential API calls):
$promise = new Promise(function () {
return fetchUserData();
});
$promise->then(function ($user) {
return fetchUserPosts($user->id);
})->then(function ($posts) {
return processPosts($posts);
})->then(function ($result) {
saveToDatabase($result);
});
Use then(null, $onRejected) or otherwise() for centralized error handling:
$promise->then(null, function ($reason) {
logError($reason);
return fallbackData();
});
Combine multiple promises with all() (from GuzzleHttp\Promise\Utils):
use GuzzleHttp\Promise\Utils;
$promises = [
fetchData('users'),
fetchData('posts'),
fetchData('comments')
];
Utils::all($promises)->then(function ($results) {
// $results = [userData, postData, commentData]
});
Cancel pending promises (e.g., timeout or user request):
$promise = new Promise(
function () { /* Long-running task */ },
function () { /* Cleanup (e.g., close DB connection) */ }
);
$promise->cancel(); // Triggers cleanup
Use wait() for blocking operations (e.g., CLI scripts):
$result = $promise->wait(); // Blocks until resolved
Guzzle HTTP Client:
Leverage GuzzleHttp\Client with promises for async requests:
$client = new \GuzzleHttp\Client();
$promise = $client->requestAsync('GET', 'https://api.example.com');
$promise->then(function ($response) {
return $response->getBody();
});
Event Loop Integration: Run the task queue in a loop (e.g., ReactPHP):
$queue = \GuzzleHttp\Promise\Utils::queue();
$loop->addPeriodicTimer(0.01, [$queue, 'run']);
Coroutines:
Use Coroutine::of() for async/await-style code:
$result = \GuzzleHttp\Promise\Coroutine::of(function () {
$data = yield fetchData();
return process($data);
});
Stack Size:
then() calls) may cause stack overflows.Unwrapping Foreign Promises:
wait()/cancel() support when forwarded.$foreignPromise = new \React\Promise\Deferred()->promise();
$guzzlePromise = new Promise(function () use ($foreignPromise) {
return $foreignPromise;
});
Blocking wait():
wait() blocks execution until the promise resolves.Cancellation Race Conditions:
Error Propagation:
then() callbacks are caught and forwarded as rejections.try/catch in callbacks if you need to handle errors differently.$state = $promise->getState(); // 'pending', 'fulfilled', or 'rejected'
$promise->then(null, function ($reason) {
var_dump($reason); // Log or inspect the rejection reason
});
then() to log values/reasons at each step:
$promise->then(function ($value) {
logger()->info("Fulfilled with: " . $value);
});
Custom Promise Classes:
Extend GuzzleHttp\Promise\Promise to add domain-specific logic:
class ApiPromise extends Promise {
public function __construct($endpoint) {
parent::__construct(function () use ($endpoint) {
return $this->fetch($endpoint);
});
}
}
Promise Utilities: Create helper functions for common patterns:
function retryPromise($promise, $maxAttempts = 3) {
return $promise->then(null, function ($reason) use ($promise, $maxAttempts) {
if ($maxAttempts > 0) {
return retryPromise($promise, $maxAttempts - 1);
}
throw $reason;
});
}
Async/Await Sugar: Build a coroutine wrapper for cleaner syntax:
function async(function $generator) {
$coroutine = \GuzzleHttp\Promise\Coroutine::of($generator);
return $coroutine->then(function ($result) {
return $result;
});
}
Task Queue:
The global task queue (Utils::queue()) must be run manually in custom event loops.
PHP Version:
Thread Safety: Promises are not thread-safe. Avoid sharing promises across threads.
How can I help you explore Laravel packages today?