2017-05-26 19:56:02 +02:00
---
2021-12-02 22:29:45 +01:00
layout: docs title: Promises permalink: /promises/
2017-05-26 19:56:02 +02:00
---
2021-12-02 22:29:45 +01:00
A `Promise` is an object representing the eventual result of an asynchronous operation. There are three states:
2017-12-21 16:40:10 +01:00
2021-12-02 22:29:45 +01:00
- **Success**: The promise resolved successfully.
- **Failure**: The promise failed.
- **Pending**: The promise has not been resolved yet.
2017-12-21 16:40:10 +01:00
2021-12-02 22:29:45 +01:00
A successful resolution is like returning a value in synchronous code while failing a promise is like throwing an
exception.
2017-12-21 16:40:10 +01:00
2021-12-02 22:29:45 +01:00
Promises are the basic unit of concurrency in asynchronous applications. In Amp they implement the `Amp\Promise`
interface. These objects should be thought of as placeholders for values or tasks that might not be complete
immediately.
2017-12-21 16:40:10 +01:00
Another way to approach asynchronous APIs is using callbacks that are passed when the operation is started.
```php
doSomething(function ($error, $value) {
if ($error) {
/* ... */
} else {
/* ... */
}
});
```
The callback approach has several drawbacks.
2021-12-02 22:29:45 +01:00
- Passing callbacks and doing further actions in them that depend on the result of the first action gets messy really
quickly.
- An explicit callback is required as input parameter to the function, and the return value is simply unused. There's no
way to use this API without involving a callback.
2017-12-21 16:40:10 +01:00
2021-12-02 22:29:45 +01:00
That's where promises come into play. They're simple placeholders that are returned and allow a callback (or several
callbacks) to be registered.
2017-12-21 16:40:10 +01:00
```php
2018-09-20 07:17:57 +02:00
doSomething()->onResolve(function ($error, $value) {
2017-12-21 16:40:10 +01:00
if ($error) {
/* ... */
} else {
/* ... */
}
});
```
2021-12-02 22:29:45 +01:00
This doesn't seem a lot better at first sight, we have just moved the callback. But in fact this enabled a lot. We can
now write helper functions like [`Amp\Promise\all()` ](https://amphp.org/amp/promises/combinators#all ) which subscribe to
several of those placeholders and combine them. We don't have to write any complicated code to combine the results of
several callbacks.
2017-12-21 16:40:10 +01:00
2021-12-02 22:29:45 +01:00
But the most important improvement of promises is that they allow
writing [coroutines ](https://amphp.org/amp/coroutines/ ), which completely eliminate the need for _any_ callbacks.
2017-12-21 16:40:10 +01:00
2021-12-02 22:29:45 +01:00
Coroutines make use of PHP's generators. Every time a promise is `yield` ed, the coroutine subscribes to the promise and
automatically continues it once the promise resolved. On successful resolution the coroutine will send the resolution
value into the generator using [`Generator::send()` ](https://secure.php.net/generator.send ). On failure it will throw
the exception into the generator using [`Generator::throw()` ](https://secure.php.net/generator.throw ). This allows
writing asynchronous code almost like synchronous code.
2017-03-20 21:21:22 +01:00
2017-05-26 21:13:34 +02:00
{:.note}
2017-12-21 16:40:10 +01:00
> Amp's `Promise` interface **does not** conform to the "Thenables" abstraction common in JavaScript promise implementations. Chaining `.then()` calls is a suboptimal method for avoiding callback hell in a world with generator coroutines. Instead, Amp utilizes PHP generators as described above.
2017-03-20 21:21:22 +01:00
>
2017-12-21 16:40:10 +01:00
> However, as ReactPHP is another wide-spread implementation, we also accept any `React\Promise\PromiseInterface` where we accept instances of `Amp\Promise`. In case of custom implementations not implementing `React\Promise\PromiseInterface`, `Amp\Promise\adapt()` can be used to adapt any object having a `then` or `done` method.
2017-03-20 21:21:22 +01:00
## Promise Consumption
```php
interface Promise {
2017-03-21 17:23:37 +01:00
public function onResolve(callable $onResolve);
2017-03-20 21:21:22 +01:00
}
```
2021-12-02 22:29:45 +01:00
In its simplest form the `Amp\Promise` aggregates callbacks for dealing with results once they eventually resolve. While
most code will not interact with this API directly thanks to [coroutines ](../coroutines/ ), let's take a quick look at
the one simple API method exposed on `Amp\Promise` implementations:
2017-03-20 21:21:22 +01:00
2017-05-27 17:16:29 +02:00
| Parameter | Callback Signature |
| ------------ | ------------------------------------------ |
| `$onResolve` | `function ($error = null, $result = null)` |
2017-03-20 21:21:22 +01:00
2021-12-02 22:29:45 +01:00
`Amp\Promise::onResolve()` accepts an error-first callback. This callback is responsible for reacting to the eventual
result represented by the promise placeholder. For example:
2017-03-20 21:21:22 +01:00
```php
< ?php
$promise = someFunctionThatReturnsAPromise();
2017-03-21 17:23:37 +01:00
$promise->onResolve(function (Throwable $error = null, $result = null) {
2017-03-20 21:21:22 +01:00
if ($error) {
printf(
"Something went wrong:\n%s\n",
$error->getMessage()
);
} else {
printf(
"Hurray! Our result is:\n%s\n",
print_r($result, true)
);
}
});
```
2021-12-02 22:29:45 +01:00
Those familiar with JavaScript code generally reflect that the above interface quickly devolves
into ["callback hell" ](http://callbackhell.com/ ), and they're correct. We will shortly see how to avoid this problem in
the [coroutines ](../coroutines/README.md ) section.
2017-03-20 21:21:22 +01:00
## Promise Creation
2021-12-02 22:29:45 +01:00
Promises can be created in several different ways. Most code will
use [`Amp\call()` ](https://amphp.org/amp/coroutines/helpers#call ) which takes a function and runs it as coroutine if it
returns a `Generator` .
2017-12-21 16:40:10 +01:00
### Success and Failure
2021-12-02 22:29:45 +01:00
Sometimes values are immediately available. This might be due to them being cached, but can also be the case if an
interface mandates a promise to be returned to allow for async I/O but the specific implementation always having the
result directly available. In these cases `Amp\Success` and `Amp\Failure` can be used to construct an immediately
resolved promise. `Amp\Success` accepts a resolution value. `Amp\Failure` accepts an exception as failure reason.
2017-12-21 16:40:10 +01:00
### Deferred
{:.note}
> The `Deferred` API described below is an advanced API that many applications probably don't need. Use [`Amp\call()`](https://amphp.org/amp/coroutines/helpers#call) or [promise combinators](https://amphp.org/amp/promises/combinators) instead where possible.
2021-12-02 22:29:45 +01:00
`Amp\Deferred` is the abstraction responsible for resolving future values once they become available. A library that
resolves values asynchronously creates an `Amp\Deferred` and uses it to return an `Amp\Promise` to API consumers. Once
the async library determines that the value is ready it resolves the promise held by the API consumer using methods on
the linked promisor.
2017-03-20 21:21:22 +01:00
```php
2018-06-18 20:11:15 +02:00
final class Deferred
{
2017-03-20 21:21:22 +01:00
public function promise(): Promise;
public function resolve($result = null);
2017-05-27 17:16:29 +02:00
public function fail(Throwable $error);
2017-03-20 21:21:22 +01:00
}
```
#### `promise()`
2021-12-02 22:29:45 +01:00
Returns the corresponding `Promise` instance. `Deferred` and `Promise` are separated, so the consumer of the promise
can't fulfill it. You should always return `$deferred->promise()` to API consumers. If you're passing `Deferred` objects
around, you're probably doing something wrong.
2017-05-27 17:16:29 +02:00
2017-03-20 21:21:22 +01:00
#### `resolve()`
2021-12-02 22:29:45 +01:00
Resolves the promise with the first parameter as value, otherwise `null` . If a `Amp\Promise` is passed, the resolution
will wait until the passed promise has been resolved. Invokes all registered `Promise::onResolve()` callbacks.
2017-03-20 21:21:22 +01:00
#### `fail()`
2021-12-02 22:29:45 +01:00
Makes the promise fail. Invokes all registered `Promise::onResolve()` callbacks with the passed `Throwable` as `$error`
argument.
2017-03-20 21:21:22 +01:00
2021-12-02 22:29:45 +01:00
Here's a simple example of an async value producer `asyncMultiply()` creating a deferred and returning the associated
promise to its API consumer.
2017-03-20 21:21:22 +01:00
```php
< ?php // Example async producer using promisor
use Amp\Loop;
2018-06-18 20:11:15 +02:00
function asyncMultiply($x, $y)
{
2017-03-20 21:21:22 +01:00
// Create a new promisor
2021-12-02 22:29:45 +01:00
$deferred = new Amp\DeferredFuture;
2017-03-20 21:21:22 +01:00
// Resolve the async result one second from now
Loop::delay($msDelay = 1000, function () use ($deferred, $x, $y) {
$deferred->resolve($x * $y);
});
return $deferred->promise();
}
$promise = asyncMultiply(6, 7);
2017-05-02 17:17:11 +02:00
$result = Amp\Promise\wait($promise);
2018-06-18 20:11:15 +02:00
2017-03-20 21:21:22 +01:00
var_dump($result); // int(42)
```