Skip to content

Commit 06fd353

Browse files
committed
Merge pull request #6 from kelunik/spec
Specification
2 parents 082b89a + 161b64a commit 06fd353

File tree

1 file changed

+79
-2
lines changed

1 file changed

+79
-2
lines changed

README.md

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,80 @@
1-
# Whenable
1+
# Awaitable
22

3-
The purpose of this proposal is to provide a common interface for simple placeholder objects returned from async operations. This will allow libraries and components from different vendors to create coroutines regardless of the library. This proposal is not designed to replace promise implementations that may be chained. Instead, this interface may be extended by promise (future, delayed) implementations.
3+
The purpose of this proposal is to provide a common interface for simple placeholder objects returned from async operations. This will allow libraries and components from different vendors to create coroutines regardless of the used placeholder implementation. This proposal is not designed to replace promise implementations that may be chained. Instead, this interface may be extended by promise implementations.
4+
5+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
6+
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
7+
interpreted as described in [RFC 2119][].
8+
9+
An `Awaitable` represents the eventual result of an asynchronous operation. Interaction with an `Awaitable` happens through its `when` method, which registers a callback to receive either an `Awaitable`'s eventual value or the reason why the `Awaitable` has failed. They're basically the same as a `Promise` in JavaScript's [Promises/A+ specification]([Promises/A+](https://promisesaplus.com/)), but the interaction with them is different as the following paragraphs show.
10+
11+
This specification defines the absolute minimums for interoperable coroutines, which can be implemented in PHP using generators. Further methods like a `watch` method for progress updates or `then` for chainability are out of scope of this specification. The name `Awaitable` works well, especially when PHP is extended to support `await`, and is also [already used in HHVM](https://docs.hhvm.com/hack/reference/interface/HH.Awaitable/).
12+
13+
`Awaitable` is the fundamental primitive in asynchronous programming. It should be as lightweight as possible, as any cost adds up significantly. The existing Promises/A+ specification conflicts with the goal of lightweight primitives. This specification uses `when`, so existing implementations can continue to provide `then` for chainability.
14+
15+
Coroutines should be the preferred way of writing asynchronous code, as it allows the use of `try` / `catch` and doesn't result in a so-called callback hell. Coroutines do not use the returned `Promise` of a `then` callback, but returning a `Promise` is required by Promises/A+. Thus there's a lot of useless object creations, which aren't cheap and add up.
16+
17+
This specification does not deal with how to create, succeed or fail `Awaitable`s, as only the consumption of `Awaitable`s is required to be interoperable.
18+
19+
## Terminology
20+
21+
1. _Awaitable_ is an object implementing `Interop\Async\Awaitable` and conforming to this specification.
22+
1. _Value_ is any legal PHP value (including `null`), except an instance of `Interop\Async\Awaitable`.
23+
1. _Error_ is any value that can be thrown using the `throw` statement.
24+
1. _Reason_ is an error indicating why an `Awaitable` has failed.
25+
26+
## States
27+
28+
An `Awaitable` MUST be in one of three states: `pending`, `succeeded`, `failed`.
29+
30+
| A promise in … state |   |
31+
|----------------------|--------|
32+
|`pending` | <ul><li>MAY transition to either the `succeeded` or `failed` state.</li></ul> |
33+
|`succeeded`| <ul><li>MUST NOT transition to any other state.</li><li>MUST have a value which MUST NOT change.*</li></ul> |
34+
|`failed` | <ul><li>MUST NOT transition to any other state.</li><li>MUST have a reason which MUST NOT change.*</li></ul> |
35+
36+
* _Must not change_ refers to the _reference_ being immutable in case of an object, _not the object itself_ being immutable.
37+
38+
## Consumption
39+
40+
An `Awaitable` MUST implement `Interop\Async\Awaitable` and thus provide a `when` method to access its current or eventual value or reason.
41+
42+
```php
43+
<?php
44+
45+
namespace Interop\Async;
46+
47+
/**
48+
* Representation of a the future value of an asynchronous operation.
49+
*/
50+
interface Awaitable
51+
{
52+
/**
53+
* Registers a callback to be invoked when the awaitable is resolved.
54+
*
55+
* @param callable(\Throwable|\Exception|null $exception, mixed $result) $onResolved
56+
*
57+
* @return void
58+
*/
59+
public function when(callable $onResolved);
60+
}
61+
```
62+
63+
The `when` method MUST accept at least one argument:
64+
65+
`$callback` – A callable conforming to the following signature:
66+
67+
```php
68+
function($error, $value) { /* ... */ }
69+
```
70+
71+
Any implementation MUST at least provide these two parameters. The implementation MAY extend the `Awaitable` interface with additional parameters passed to the callback. Further arguments to `when` MUST have default values, so `when` can always be called with only one argument. `when` MUST NOT return a value.
72+
73+
> **NOTE:** The signature doesn't specify a type for `$error`. This is due to the new `Throwable` interface introduced in PHP 7. As this specification is PHP 5 compatible, we can use neither `Throwable` nor `Exception`.
74+
75+
All registered callbacks MUST be executed in the order they were registered. If one of the callbacks throws an `Exception` or `Throwable`, it MUST be rethrown in a callable passed to `Loop::defer` so `Loop::onError` can be properly invoked by the loop. `Loop` refers to the [global event loop accessor](https://github.com/async-interop/event-loop/blob/master/src/Loop.php). The `Awaitable` implementation MUST then continue to call the remaining callbacks with the original reason.
76+
77+
If an `Awaitable` is resolved with another `Awaitable`, the `Awaitable` MUST keep in pending state until the passed `Awaitable` is resolved. Thus, the value of an `Awaitable` can never be an `Awaitable`. An `Awaitable` MUST NOT be resolved with itself.
478

579
## Contributors
680

@@ -9,5 +83,8 @@ The purpose of this proposal is to provide a common interface for simple placeho
983
* [Bob Weinand](https://github.com/bwoebi)
1084
* [Cees-Jan Kiewiet](https://github.com/WyriHaximus)
1185
* [Christopher Pitt](https://github.com/assertchris)
86+
* [Daniel Lowrey](https://github.com/rdlowrey)
1287
* [Niklas Keller](https://github.com/kelunik)
1388
* [Stephen M. Coakley](https://github.com/coderstephen)
89+
90+
[RFC 2119]: http://tools.ietf.org/html/rfc2119

0 commit comments

Comments
 (0)