Skip to content

Commit 8d2c883

Browse files
committed
Add documentation
1 parent eee7ea0 commit 8d2c883

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

README.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,185 @@ A trivial implementation of timeouts for `Promise`s, built on top of [React PHP]
44

55
> Note: This project is in early alpha stage! Feel free to report any issues you encounter.
66
7+
## Usage
8+
9+
This lightweight library consists only of a few simple functions.
10+
All functions reside under the `Clue\Promise\Timer` namespace.
11+
12+
The below examples assume you use an import statement similar to this:
13+
14+
```php
15+
use Clue\Promise\Timer;
16+
17+
Timer\timeout(…);
18+
```
19+
20+
Alternatively, you can also refer to them with their fully-qualified name:
21+
22+
```php
23+
\Clue\Promise\Timer\timeout(…);
24+
```
25+
26+
### timeout()
27+
28+
The `timeout(PromiseInterface $promise, $time, LoopInterface $loop)` function
29+
can be used to *cancel* operations that take *too long*.
30+
You need to pass in an input `$promise` that represents a pending operation and timeout parameters.
31+
It returns a new `Promise` with the following resolution behavior:
32+
33+
* If the input `$promise` resolves before `$time` seconds, resolve the resulting promise with its fulfillment value.
34+
* If the input `$promise` rejects before `$time` seconds, reject the resulting promise with its rejection value.
35+
* If the input `$promise` does not settle before `$time` seconds, *cancel* the operation and reject the resulting promise with a [`TimeoutException`](#timeoutexception).
36+
37+
A common use case for handling only resolved values looks like this:
38+
39+
```php
40+
$promise = accessSomeRemoteResource();
41+
Timer\timeout($promise, 10.0, $loop)->then(function ($value) {
42+
// the operation finished within 10.0 seconds
43+
});
44+
```
45+
46+
A more complete example could look like this:
47+
48+
```php
49+
$promise = accessSomeRemoteResource();
50+
Timer\timeout($promise, 10.0, $loop)->then(
51+
function ($value) {
52+
// the operation finished within 10.0 seconds
53+
},
54+
function ($error) {
55+
if ($error instanceof Timer\TimeoutException) {
56+
// the operation has failed due to a timeout
57+
} else {
58+
// the input operation has failed due to some other error
59+
}
60+
throw $error;
61+
}
62+
);
63+
```
64+
65+
#### Timeout cancellation
66+
67+
As discussed above, the [`timeout()`](#timeout) function will *cancel* the
68+
underlying operation if it takes *too long*.
69+
This means that you can be sure the resulting promise will then be rejected
70+
with a [`TimeoutException`](#timeoutexception).
71+
72+
However, what happens to the underlying input `$promise` is a bit more tricky:
73+
Once the timer fires, we will try to call
74+
[`$promise->cancel()`](https://github.com/reactphp/promise#cancellablepromiseinterfacecancel)
75+
on the input `$promise` which in turn invokes its [cancellation handler](#cancellation-handler).
76+
77+
This means that it's actually up the input `$promise` to handle
78+
[cancellation support](https://github.com/reactphp/promise#cancellablepromiseinterface).
79+
80+
* A common use case involves cleaning up any resources like open network sockets or
81+
file handles or terminating external processes or timers.
82+
83+
* If the given input `$promise` does not support cancellation, then this is a NO-OP.
84+
This means that while the resulting promise will still be rejected, the underlying
85+
input `$promise` may still be pending and can hence continue consuming resources.
86+
87+
See the following chapter for more details on the cancellation handler.
88+
89+
#### Cancellation handler
90+
91+
For example, an implementation for the above operation could look like this:
92+
93+
```php
94+
function accessSomeRemoteResource()
95+
{
96+
return new Promise(
97+
function ($resolve, $reject) use (&$socket) {
98+
// this will be called once the promise is created
99+
// a common use case involves opening any resources and eventually resolving
100+
$socket = createSocket();
101+
$socket->on('data', function ($data) use ($resolve) {
102+
$resolve($data);
103+
});
104+
},
105+
function ($resolve, $reject) use (&$socket) {
106+
// this will be called once calling `cancel()` on this promise
107+
// a common use case involves cleaning any resources and then rejecting
108+
$socket->close();
109+
$reject(new \RuntimeException('Operation cancelled'));
110+
}
111+
);
112+
}
113+
```
114+
115+
In this example, calling `$promise->cancel()` will invoke the registered cancellation
116+
handler which then closes the network socket and rejects the `Promise` instance.
117+
118+
If no cancellation handler is passed to the `Promise` constructor, then invoking
119+
its `cancel()` method it is effectively a NO-OP.
120+
This means that it may still be pending and can hence continue consuming resources.
121+
122+
> Note: If you're stuck on legacy versions (PHP 5.3), then this is also a NO-OP,
123+
as the Promise cancellation API is currently only available in
124+
[react/promise v2.1.0](https://github.com/reactphp/promise)
125+
which in turn requires PHP 5.4 or up.
126+
It is assumed that if you're actually still stuck on PHP 5.3, resource cleanup
127+
is likely one of your smaller problems.
128+
129+
For more details on the promise cancellation, please refer to the
130+
[Promise documentation](https://github.com/reactphp/promise#cancellablepromiseinterface).
131+
132+
#### Collections
133+
134+
If you want to wait for multiple promises to resolve, you can use the normal promise primitives like this:
135+
136+
```php
137+
$promises = array(
138+
accessSomeRemoteResource(),
139+
accessSomeRemoteResource(),
140+
accessSomeRemoteResource()
141+
);
142+
143+
$promise = \React\Promise\all($promises);
144+
145+
Timer\timeout($promise, 10, $loop)->then(function ($values) {
146+
// *all* promises resolved
147+
});
148+
```
149+
150+
The applies to all promise collection primitives alike, i.e. `all()`, `race()`, `any()`, `some()` etc.
151+
152+
For more details on the promise primitives, please refer to the
153+
[Promise documentation](https://github.com/reactphp/promise#functions).
154+
155+
### resolve()
156+
157+
The `resolve($time, LoopInterface $loop)` function can be used to create a new Promise that
158+
resolves in `$time` seconds with the `$time` as the fulfillment value.
159+
160+
```php
161+
Timer\resolve(1.5, $loop)->then(function ($time) {
162+
echo 'Thanks for waiting ' . $time . ' seconds' . PHP_EOL;
163+
});
164+
```
165+
166+
### reject()
167+
168+
The `reject($time, LoopInterface $loop)` function can be used to create a new Promise
169+
which rejects in `$time` seconds with a `TimeoutException`.
170+
171+
```php
172+
Timer\reject(2.0, $loop)->then(null, function (TimeoutException $e) {
173+
echo '
174+
});
175+
```
176+
177+
This function complements the [`resolve()`](#resolve) function
178+
and can be used as a basic building block for higher-level promise consumers.
179+
180+
### TimeoutException
181+
182+
The `TimeoutException` extends PHP's built-in `RuntimeException`.
183+
184+
The `getTimeout()` method can be used to get the timeout value in seconds.
185+
7186
## Install
8187

9188
The recommended way to install this library is [through composer](http://getcomposer.org). [New to composer?](http://getcomposer.org/doc/00-intro.md)
@@ -16,6 +195,12 @@ The recommended way to install this library is [through composer](http://getcomp
16195
}
17196
```
18197

198+
> Note: If you're stuck on legacy versions (PHP 5.3), then the `cancel()` method
199+
is not available,
200+
as the Promise cancellation API is currently only available in
201+
[react/promise v2.1.0](https://github.com/reactphp/promise)
202+
which in turn requires PHP 5.4 or up.
203+
19204
## License
20205

21206
MIT

0 commit comments

Comments
 (0)