@@ -8,16 +8,10 @@ A trivial implementation of timeouts for `Promise`s, built on top of [ReactPHP](
8
8
9
9
* [ Usage] ( #usage )
10
10
* [ timeout()] ( #timeout )
11
- * [ Timeout cancellation] ( #timeout-cancellation )
12
- * [ Cancellation handler] ( #cancellation-handler )
13
- * [ Input cancellation] ( #input-cancellation )
14
- * [ Output cancellation] ( #output-cancellation )
15
- * [ Collections] ( #collections )
16
11
* [ resolve()] ( #resolve )
17
- * [ Resolve cancellation] ( #resolve-cancellation )
18
12
* [ reject()] ( #reject )
19
- * [ Reject cancellation] ( #reject-cancellation )
20
13
* [ TimeoutException] ( #timeoutexception )
14
+ * [ getTimeout()] ( #gettimeout )
21
15
* [ Install] ( #install )
22
16
* [ Tests] ( #tests )
23
17
* [ License] ( #license )
@@ -51,19 +45,26 @@ Timer\timeout(…);
51
45
52
46
### timeout()
53
47
54
- The ` timeout(PromiseInterface $promise, $time, LoopInterface $loop = null) ` function
55
- can be used to * cancel* operations that take * too long* .
56
- You need to pass in an input ` $promise ` that represents a pending operation and timeout parameters.
57
- It returns a new ` Promise ` with the following resolution behavior:
48
+ The ` timeout(PromiseInterface<mixed, Exception|mixed> $promise, float $time, ?LoopInterface $loop = null): PromiseInterface<mixed, TimeoutException|Exception|mixed> ` function can be used to
49
+ cancel operations that take * too long* .
58
50
59
- * If the input ` $promise ` resolves before ` $time ` seconds, resolve the resulting promise with its fulfillment value.
60
- * If the input ` $promise ` rejects before ` $time ` seconds, reject the resulting promise with its rejection value.
61
- * If the input ` $promise ` does not settle before ` $time ` seconds, * cancel* the operation and reject the resulting promise with a [ ` TimeoutException ` ] ( #timeoutexception ) .
51
+ You need to pass in an input ` $promise ` that represents a pending operation
52
+ and timeout parameters. It returns a new promise with the following
53
+ resolution behavior:
54
+
55
+ - If the input ` $promise ` resolves before ` $time ` seconds, resolve the
56
+ resulting promise with its fulfillment value.
57
+
58
+ - If the input ` $promise ` rejects before ` $time ` seconds, reject the
59
+ resulting promise with its rejection value.
60
+
61
+ - If the input ` $promise ` does not settle before ` $time ` seconds, * cancel*
62
+ the operation and reject the resulting promise with a [ ` TimeoutException ` ] ( #timeoutexception ) .
62
63
63
64
Internally, the given ` $time ` value will be used to start a timer that will
64
- * cancel* the pending operation once it triggers.
65
- This implies that if you pass a really small (or negative) value, it will still
66
- start a timer and will thus trigger at the earliest possible time in the future.
65
+ * cancel* the pending operation once it triggers. This implies that if you
66
+ pass a really small (or negative) value, it will still start a timer and will
67
+ thus trigger at the earliest possible time in the future.
67
68
68
69
If the input ` $promise ` is already settled, then the resulting promise will
69
70
resolve or reject immediately without starting a timer at all.
@@ -117,95 +118,25 @@ React\Promise\Timer\timeout($promise, 10.0)
117
118
;
118
119
```
119
120
120
- #### Timeout cancellation
121
-
122
- As discussed above, the [ ` timeout() ` ] ( #timeout ) function will * cancel* the
123
- underlying operation if it takes * too long* .
124
- This means that you can be sure the resulting promise will then be rejected
125
- with a [ ` TimeoutException ` ] ( #timeoutexception ) .
126
-
127
- However, what happens to the underlying input ` $promise ` is a bit more tricky:
128
- Once the timer fires, we will try to call
129
- [ ` $promise->cancel() ` ] ( https://github.com/reactphp/promise#cancellablepromiseinterfacecancel )
130
- on the input ` $promise ` which in turn invokes its [ cancellation handler] ( #cancellation-handler ) .
131
-
132
- This means that it's actually up the input ` $promise ` to handle
133
- [ cancellation support] ( https://github.com/reactphp/promise#cancellablepromiseinterface ) .
134
-
135
- * A common use case involves cleaning up any resources like open network sockets or
136
- file handles or terminating external processes or timers.
137
-
138
- * If the given input ` $promise ` does not support cancellation, then this is a NO-OP.
139
- This means that while the resulting promise will still be rejected, the underlying
140
- input ` $promise ` may still be pending and can hence continue consuming resources.
141
-
142
- See the following chapter for more details on the cancellation handler.
143
-
144
- #### Cancellation handler
145
-
146
- For example, an implementation for the above operation could look like this:
147
-
148
- ``` php
149
- function accessSomeRemoteResource()
150
- {
151
- return new Promise(
152
- function ($resolve, $reject) use (& $socket) {
153
- // this will be called once the promise is created
154
- // a common use case involves opening any resources and eventually resolving
155
- $socket = createSocket();
156
- $socket->on('data', function ($data) use ($resolve) {
157
- $resolve($data);
158
- });
159
- },
160
- function ($resolve, $reject) use (& $socket) {
161
- // this will be called once calling `cancel()` on this promise
162
- // a common use case involves cleaning any resources and then rejecting
163
- $socket->close();
164
- $reject(new \RuntimeException('Operation cancelled'));
165
- }
166
- );
167
- }
168
- ```
169
-
170
- In this example, calling ` $promise->cancel() ` will invoke the registered cancellation
171
- handler which then closes the network socket and rejects the ` Promise ` instance.
121
+ As discussed above, the [ ` timeout() ` ] ( #timeout ) function will take care of
122
+ the underlying operation if it takes * too long* . In this case, you can be
123
+ sure the resulting promise will always be rejected with a
124
+ [ ` TimeoutException ` ] ( #timeoutexception ) . On top of this, the function will
125
+ try to * cancel* the underlying operation. Responsibility for this
126
+ cancellation logic is left up to the underlying operation.
172
127
173
- If no cancellation handler is passed to the ` Promise ` constructor, then invoking
174
- its ` cancel() ` method it is effectively a NO-OP.
175
- This means that it may still be pending and can hence continue consuming resources.
128
+ - A common use case involves cleaning up any resources like open network
129
+ sockets or file handles or terminating external processes or timers.
176
130
177
- For more details on the promise cancellation, please refer to the
178
- [ Promise documentation ] ( https://github.com/reactphp/ promise#cancellablepromiseinterface ) .
179
-
180
- #### Input cancellation
131
+ - If the given input ` $ promise` does not support cancellation, then this is a
132
+ NO-OP. This means that while the resulting promise will still be rejected,
133
+ the underlying input ` $promise ` may still be pending and can hence continue
134
+ consuming resources
181
135
182
- Irrespective of the timeout handling, you can also explicitly ` cancel() ` the
183
- input ` $promise ` at any time.
184
- This means that the ` timeout() ` handling does not affect cancellation of the
185
- input ` $promise ` , as demonstrated in the following example:
186
-
187
- ``` php
188
- $promise = accessSomeRemoteResource();
189
- $timeout = React\Promise\Timer\timeout($promise, 10.0);
190
-
191
- $promise->cancel();
192
- ```
193
-
194
- The registered [ cancellation handler] ( #cancellation-handler ) is responsible for
195
- handling the ` cancel() ` call:
196
-
197
- * A described above, a common use involves resource cleanup and will then * reject*
198
- the ` Promise ` .
199
- If the input ` $promise ` is being rejected, then the timeout will be aborted
200
- and the resulting promise will also be rejected.
201
- * If the input ` $promise ` is still pending, then the timout will continue
202
- running until the timer expires.
203
- The same happens if the input ` $promise ` does not register a
204
- [ cancellation handler] ( #cancellation-handler ) .
205
-
206
- #### Output cancellation
207
-
208
- Similarily, you can also explicitly ` cancel() ` the resulting promise like this:
136
+ On top of this, the returned promise is implemented in such a way that it can
137
+ be cancelled when it is still pending. Cancelling a pending promise will
138
+ cancel the underlying operation. As discussed above, responsibility for this
139
+ cancellation logic is left up to the underlying operation.
209
140
210
141
``` php
211
142
$promise = accessSomeRemoteResource();
@@ -214,54 +145,11 @@ $timeout = React\Promise\Timer\timeout($promise, 10.0);
214
145
$timeout->cancel();
215
146
```
216
147
217
- Note how this looks very similar to the above [ input cancellation] ( #input-cancellation )
218
- example. Accordingly, it also behaves very similar.
219
-
220
- Calling ` cancel() ` on the resulting promise will merely try
221
- to ` cancel() ` the input ` $promise ` .
222
- This means that we do not take over responsibility of the outcome and it's
223
- entirely up to the input ` $promise ` to handle cancellation support.
224
-
225
- The registered [ cancellation handler] ( #cancellation-handler ) is responsible for
226
- handling the ` cancel() ` call:
227
-
228
- * As described above, a common use involves resource cleanup and will then * reject*
229
- the ` Promise ` .
230
- If the input ` $promise ` is being rejected, then the timeout will be aborted
231
- and the resulting promise will also be rejected.
232
- * If the input ` $promise ` is still pending, then the timout will continue
233
- running until the timer expires.
234
- The same happens if the input ` $promise ` does not register a
235
- [ cancellation handler] ( #cancellation-handler ) .
236
-
237
- To re-iterate, note that calling ` cancel() ` on the resulting promise will merely
238
- try to cancel the input ` $promise ` only.
239
- It is then up to the cancellation handler of the input promise to settle the promise.
240
- If the input promise is still pending when the timeout occurs, then the normal
241
- [ timeout cancellation] ( #timeout-cancellation ) handling will trigger, effectively rejecting
242
- the output promise with a [ ` TimeoutException ` ] ( #timeoutexception ) .
243
-
244
- This is done for consistency with the [ timeout cancellation] ( #timeout-cancellation )
245
- handling and also because it is assumed this is often used like this:
246
-
247
- ``` php
248
- $timeout = React\Promise\Timer\timeout(accessSomeRemoteResource(), 10.0);
249
-
250
- $timeout->cancel();
251
- ```
252
-
253
- As described above, this example works as expected and cleans up any resources
254
- allocated for the input ` $promise ` .
255
-
256
- Note that if the given input ` $promise ` does not support cancellation, then this
257
- is a NO-OP.
258
- This means that while the resulting promise will still be rejected after the
259
- timeout, the underlying input ` $promise ` may still be pending and can hence
260
- continue consuming resources.
261
-
262
- #### Collections
148
+ For more details on the promise cancellation, please refer to the
149
+ [ Promise documentation] ( https://github.com/reactphp/promise#cancellablepromiseinterface ) .
263
150
264
- If you want to wait for multiple promises to resolve, you can use the normal promise primitives like this:
151
+ If you want to wait for multiple promises to resolve, you can use the normal
152
+ promise primitives like this:
265
153
266
154
``` php
267
155
$promises = array(
@@ -270,22 +158,23 @@ $promises = array(
270
158
accessSomeRemoteResource()
271
159
);
272
160
273
- $promise = \ React\Promise\all($promises);
161
+ $promise = React\Promise\all($promises);
274
162
275
163
React\Promise\Timer\timeout($promise, 10)->then(function ($values) {
276
164
// *all* promises resolved
277
165
});
278
166
```
279
167
280
- The applies to all promise collection primitives alike, i.e. ` all() ` , ` race() ` , ` any() ` , ` some() ` etc.
168
+ The applies to all promise collection primitives alike, i.e. ` all() ` ,
169
+ ` race() ` , ` any() ` , ` some() ` etc.
281
170
282
171
For more details on the promise primitives, please refer to the
283
172
[ Promise documentation] ( https://github.com/reactphp/promise#functions ) .
284
173
285
174
### resolve()
286
175
287
- The ` resolve($time, LoopInterface $loop = null) ` function can be used to create a new Promise that
288
- resolves in ` $time ` seconds with the ` $time ` as the fulfillment value.
176
+ The ` resolve(float $time, ? LoopInterface $loop = null): PromiseInterface<float, RuntimeException> ` function can be used to
177
+ create a new promise that resolves in ` $time ` seconds with the ` $time ` as the fulfillment value.
289
178
290
179
``` php
291
180
React\Promise\Timer\resolve(1.5)->then(function ($time) {
@@ -294,32 +183,30 @@ React\Promise\Timer\resolve(1.5)->then(function ($time) {
294
183
```
295
184
296
185
Internally, the given ` $time ` value will be used to start a timer that will
297
- resolve the promise once it triggers.
298
- This implies that if you pass a really small (or negative) value, it will still
299
- start a timer and will thus trigger at the earliest possible time in the future.
186
+ resolve the promise once it triggers. This implies that if you pass a really
187
+ small (or negative) value, it will still start a timer and will thus trigger
188
+ at the earliest possible time in the future.
300
189
301
190
This function takes an optional ` LoopInterface|null $loop ` parameter that can be used to
302
191
pass the event loop instance to use. You can use a ` null ` value here in order to
303
192
use the [ default loop] ( https://github.com/reactphp/event-loop#loop ) . This value
304
193
SHOULD NOT be given unless you're sure you want to explicitly use a given event
305
194
loop instance.
306
195
307
- #### Resolve cancellation
308
-
309
- You can explicitly ` cancel() ` the resulting timer promise at any time:
196
+ The returned promise is implemented in such a way that it can be cancelled
197
+ when it is still pending. Cancelling a pending promise will reject its value
198
+ with a ` RuntimeException ` and clean up any pending timers.
310
199
311
200
``` php
312
201
$timer = React\Promise\Timer\resolve(2.0);
313
202
314
203
$timer->cancel();
315
204
```
316
205
317
- This will abort the timer and * reject* with a ` RuntimeException ` .
318
-
319
206
### reject()
320
207
321
- The ` reject($time, LoopInterface $loop = null) ` function can be used to create a new Promise
322
- which rejects in ` $time ` seconds with a ` TimeoutException ` .
208
+ The ` reject(float $time, ? LoopInterface $loop = null): PromiseInterface<void, TimeoutException|RuntimeException> ` function can be used to
209
+ create a new promise which rejects in ` $time ` seconds with a ` TimeoutException ` .
323
210
324
211
``` php
325
212
React\Promise\Timer\reject(2.0)->then(null, function (React\Promise\Timer\TimeoutException $e) {
@@ -328,36 +215,35 @@ React\Promise\Timer\reject(2.0)->then(null, function (React\Promise\Timer\Timeou
328
215
```
329
216
330
217
Internally, the given ` $time ` value will be used to start a timer that will
331
- reject the promise once it triggers.
332
- This implies that if you pass a really small (or negative) value, it will still
333
- start a timer and will thus trigger at the earliest possible time in the future.
218
+ reject the promise once it triggers. This implies that if you pass a really
219
+ small (or negative) value, it will still start a timer and will thus trigger
220
+ at the earliest possible time in the future.
334
221
335
222
This function takes an optional ` LoopInterface|null $loop ` parameter that can be used to
336
223
pass the event loop instance to use. You can use a ` null ` value here in order to
337
224
use the [ default loop] ( https://github.com/reactphp/event-loop#loop ) . This value
338
225
SHOULD NOT be given unless you're sure you want to explicitly use a given event
339
226
loop instance.
340
227
341
- This function complements the [ ` resolve() ` ] ( #resolve ) function
342
- and can be used as a basic building block for higher-level promise consumers.
343
-
344
- #### Reject cancellation
345
-
346
- You can explicitly ` cancel() ` the resulting timer promise at any time:
228
+ The returned promise is implemented in such a way that it can be cancelled
229
+ when it is still pending. Cancelling a pending promise will reject its value
230
+ with a ` RuntimeException ` and clean up any pending timers.
347
231
348
232
``` php
349
233
$timer = React\Promise\Timer\reject(2.0);
350
234
351
235
$timer->cancel();
352
236
```
353
237
354
- This will abort the timer and * reject* with a ` RuntimeException ` .
355
-
356
238
### TimeoutException
357
239
358
240
The ` TimeoutException ` extends PHP's built-in ` RuntimeException ` .
359
241
360
- The ` getTimeout() ` method can be used to get the timeout value in seconds.
242
+
243
+ #### getTimeout()
244
+
245
+ The ` getTimeout(): float ` method can be used to
246
+ get the timeout value in seconds.
361
247
362
248
## Install
363
249
0 commit comments