guzzlehttp/promises
Guzzle Promises is a small PHP promise library that powers Guzzle's asynchronous requests. It provides promise chaining, synchronous waiting, cancellation, and helpers for coordinating groups of promises. Install it directly when you need promise composition without the full HTTP client.
Utils::all() with a bounded concurrency window makes it roughly the max. This is a page-speed and conversion lever, and it's the single most defensible reason to care about this package as a PM.guzzlehttp/guzzle, which Laravel's Http facade wraps — so Http::pool() and requestAsync() are available without adding a direct dependency. Adoption cost is near zero; the decision is really "do we expose async composition to feature teams, or keep everything serial for simplicity?"Utils::some(), Utils::any(), and Utils::settle() let you define a partial-failure product behavior explicitly: "show results even if the recommendations vendor times out." That is a UX and SLA decision a PM should own, and the API shape maps directly onto it.Each::of() and Utils::all() accept a concurrency config. This is how you cap fan-out per request so you don't hammer a partner's rate limit or blow up your own egress and worker memory during a traffic spike. It's a policy knob, not just a technical one.Utils::queue()->run() gives engineering a way to drive promise resolution in tests without sleeping. Cheaper, faster CI and fewer flaky async bugs — worth citing when defending the dependency in a review.Is to State and deprecates Is, because PHP 8.6 deprecates is as a class name. Product relevance: this is a small, mechanical migration (a find-and-replace in application and test code), and it unblocks us from being pinned out of a future PHP upgrade by a transitive dependency. It's a maintenance-budget line item, not a feature — but it should be scheduled deliberately rather than discovered during a framework upgrade.Adopt (or, more accurately, formally sanction) when:
Http client and have request paths issuing two or more independent outbound calls that don't share data dependencies.is class-name deprecation before it lands is the signal you want from a transitive dependency you can't easily remove.Look elsewhere when:
Three caveats to record as risks in the decision log: getState() describes how a promise was settled, not the eventual outcome when it was resolved with another promise (see upstream issue #101) — so error-handling UX built on state polling is fragile, and the 3.1.0 Is → State rename does not change that semantics, only the class name; cancellation is cooperative, meaning the underlying transport has to honor it, which not every partner SDK does; and Is is now deprecated, so any code written against it today accrues a second migration — write new code against State immediately and treat Is removal as a known future breaking change rather than a surprise.
For executives (one paragraph): Users experience our latency as the sum of every partner call we make on a page. This library lets us issue those calls concurrently and resolve them together, which turns a six-call sequential chain into roughly one round trip — a direct improvement to p95 page load and timeout rates, at zero licensing cost. It's MIT-licensed, has ~7.7k GitHub stars, is actively maintained with releases through 2026, and is already inside our dependency tree via Guzzle and Laravel's HTTP client, so we're standardizing on something we already ship rather than adding a vendor. The cost is a small amount of engineering complexity in how we handle partial failures, which we should treat as an explicit product decision rather than an accident. The latest release (3.1.0) is housekeeping — it renames an internal state-check class ahead of a PHP 8.6 deprecation — which we should read as a positive maintenance signal, not a new capability.
For engineering (one paragraph): guzzlehttp/promises is the promise implementation behind guzzlehttp/guzzle; Http::pool() and requestAsync() already return PromiseInterface. We get then() chaining, wait(), cooperative cancellation, Utils::all/some/any/settle, and Each::of() with a concurrency cap. Pin ^3.0 (PHP >=7.4,<8.7; 2.5 stays on maintenance for older platforms) and pin at least ^3.1 for new work. In 3.1.0 the Is state-check class is deprecated in favor of State (PHP 8.6 deprecates is as a class name), so use State::* in new code and schedule a find-and-replace for existing Is::* usages and any test assertions or dashboards keyed on it. Constrain fan-out with explicit concurrency limits and per-partner timeouts, don't poll getState() on promises resolved with other promises, and use Utils::queue()->run() to keep tests deterministic instead of sleeping. Keep it out of CPU-bound paths and out of anything that should be a queued job.
How can I help you explore Laravel packages today?