1
- # Async
1
+ # Async Utilities
2
2
3
3
[ ![ CI status] ( https://github.com/reactphp/async/workflows/CI/badge.svg )] ( https://github.com/reactphp/async/actions )
4
4
@@ -16,6 +16,7 @@ an event loop, it can be used with this library.
16
16
** Table of Contents**
17
17
18
18
* [ Usage] ( #usage )
19
+ * [ async()] ( #async )
19
20
* [ await()] ( #await )
20
21
* [ coroutine()] ( #coroutine )
21
22
* [ parallel()] ( #parallel )
@@ -53,6 +54,146 @@ use React\Async;
53
54
Async\await(…);
54
55
```
55
56
57
+ ### async()
58
+
59
+ The ` async(callable $function): callable ` function can be used to
60
+ return an async function for a function that uses [ ` await() ` ] ( #await ) internally.
61
+
62
+ This function is specifically designed to complement the [ ` await() ` function] ( #await ) .
63
+ The [ ` await() ` function] ( #await ) can be considered * blocking* from the
64
+ perspective of the calling code. You can avoid this blocking behavior by
65
+ wrapping it in an ` async() ` function call. Everything inside this function
66
+ will still be blocked, but everything outside this function can be executed
67
+ asynchronously without blocking:
68
+
69
+ ``` php
70
+ Loop::addTimer(0.5, React\Async\async(function() {
71
+ echo 'a';
72
+ React\async\await(React\Promise\Timer\sleep(1.0));
73
+ echo 'c';
74
+ }));
75
+
76
+ Loop::addTimer(1.0, fn() => echo 'b');
77
+
78
+ // prints "a" at t=0.5s
79
+ // prints "b" at t=1.0s
80
+ // prints "c" at t=1.5s
81
+ ```
82
+
83
+ See also the [ ` await() ` function] ( #await ) for more details.
84
+
85
+ Note that this function only works in tandem with the [ ` await() ` function] ( #await ) .
86
+ In particular, this function does not "magically" make any blocking function
87
+ non-blocking:
88
+
89
+ ``` php
90
+ Loop::addTimer(0.5, React\Async\async(function() {
91
+ echo 'a';
92
+ sleep(1); // broken: using PHP's blocking sleep() for demonstration purposes
93
+ echo 'c';
94
+ }));
95
+
96
+ Loop::addTimer(1.0, fn() => echo 'b');
97
+
98
+ // prints "a" at t=0.5s
99
+ // prints "c" at t=1.5s: Correct timing, but wrong order
100
+ // prints "b" at t=1.5s: Triggered too late because it was blocked
101
+ ```
102
+
103
+ As an alternative, you should always make sure to use this function in tandem
104
+ with the [ ` await() ` function] ( #await ) and an async API returning a promise
105
+ as shown in the previous example.
106
+
107
+ The ` async() ` function is specifically designed for cases where it is used
108
+ as a callback (such as an event loop timer, event listener, or promise
109
+ callback). For this reason, it returns a new function wrapping the given
110
+ ` $function ` instead of directly invoking it and returning its value.
111
+
112
+ ``` php
113
+ use function React\Async\async;
114
+
115
+ Loop::addTimer(1.0, async(function () { … }));
116
+ $connection->on('close', async(function () { … }));
117
+ $stream->on('data', async(function ($data) { … }));
118
+ $promise->then(async(function (int $result) { … }));
119
+ ```
120
+
121
+ You can invoke this wrapping function to invoke the given ` $function ` with
122
+ any arguments given as-is. The function will always return a Promise which
123
+ will be fulfilled with whatever your ` $function ` returns. Likewise, it will
124
+ return a promise that will be rejected if you throw an ` Exception ` or
125
+ ` Throwable ` from your ` $function ` . This allows you to easily create
126
+ Promise-based functions:
127
+
128
+ ``` php
129
+ $promise = React\Async\async(function (): int {
130
+ $browser = new React\Http\Browser();
131
+ $urls = [
132
+ 'https://example.com/alice',
133
+ 'https://example.com/bob'
134
+ ];
135
+
136
+ $bytes = 0;
137
+ foreach ($urls as $url) {
138
+ $response = React\Async\await($browser->get($url));
139
+ assert($response instanceof Psr\Http\Message\ResponseInterface);
140
+ $bytes += $response->getBody()->getSize();
141
+ }
142
+ return $bytes;
143
+ })();
144
+
145
+ $promise->then(function (int $bytes) {
146
+ echo 'Total size: ' . $bytes . PHP_EOL;
147
+ }, function (Exception $e) {
148
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
149
+ });
150
+ ```
151
+
152
+ The previous example uses [ ` await() ` ] ( #await ) inside a loop to highlight how
153
+ this vastly simplifies consuming asynchronous operations. At the same time,
154
+ this naive example does not leverage concurrent execution, as it will
155
+ essentially "await" between each operation. In order to take advantage of
156
+ concurrent execution within the given ` $function ` , you can "await" multiple
157
+ promises by using a single [ ` await() ` ] ( #await ) together with Promise-based
158
+ primitives like this:
159
+
160
+ ``` php
161
+ $promise = React\Async\async(function (): int {
162
+ $browser = new React\Http\Browser();
163
+ $urls = [
164
+ 'https://example.com/alice',
165
+ 'https://example.com/bob'
166
+ ];
167
+
168
+ $promises = [];
169
+ foreach ($urls as $url) {
170
+ $promises[] = $browser->get($url);
171
+ }
172
+
173
+ try {
174
+ $responses = React\Async\await(React\Promise\all($promises));
175
+ } catch (Exception $e) {
176
+ foreach ($promises as $promise) {
177
+ $promise->cancel();
178
+ }
179
+ throw $e;
180
+ }
181
+
182
+ $bytes = 0;
183
+ foreach ($responses as $response) {
184
+ assert($response instanceof Psr\Http\Message\ResponseInterface);
185
+ $bytes += $response->getBody()->getSize();
186
+ }
187
+ return $bytes;
188
+ })();
189
+
190
+ $promise->then(function (int $bytes) {
191
+ echo 'Total size: ' . $bytes . PHP_EOL;
192
+ }, function (Exception $e) {
193
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
194
+ });
195
+ ```
196
+
56
197
### await()
57
198
58
199
The ` await(PromiseInterface $promise): mixed ` function can be used to
@@ -63,8 +204,27 @@ $result = React\Async\await($promise);
63
204
```
64
205
65
206
This function will only return after the given ` $promise ` has settled, i.e.
66
- either fulfilled or rejected. While the promise is pending, this function will
67
- suspend the fiber it's called from until the promise is settled.
207
+ either fulfilled or rejected. While the promise is pending, this function
208
+ can be considered * blocking* from the perspective of the calling code.
209
+ You can avoid this blocking behavior by wrapping it in an [ ` async() ` function] ( #async )
210
+ call. Everything inside this function will still be blocked, but everything
211
+ outside this function can be executed asynchronously without blocking:
212
+
213
+ ``` php
214
+ Loop::addTimer(0.5, React\Async\async(function() {
215
+ echo 'a';
216
+ React\async\await(React\Promise\Timer\sleep(1.0));
217
+ echo 'c';
218
+ }));
219
+
220
+ Loop::addTimer(1.0, fn() => echo 'b');
221
+
222
+ // prints "a" at t=0.5s
223
+ // prints "b" at t=1.0s
224
+ // prints "c" at t=1.5s
225
+ ```
226
+
227
+ See also the [ ` async() ` function] ( #async ) for more details.
68
228
69
229
Once the promise is fulfilled, this function will return whatever the promise
70
230
resolved to.
@@ -125,10 +285,11 @@ when the promise is fulfilled. The `yield` statement returns whatever the
125
285
promise is fulfilled with. If the promise is rejected, it will throw an
126
286
` Exception ` or ` Throwable ` .
127
287
128
- The ` coroutine() ` function will always return a Proimise which will be
288
+ The ` coroutine() ` function will always return a Promise which will be
129
289
fulfilled with whatever your ` $function ` returns. Likewise, it will return
130
290
a promise that will be rejected if you throw an ` Exception ` or ` Throwable `
131
- from your ` $function ` . This allows you easily create Promise-based functions:
291
+ from your ` $function ` . This allows you to easily create Promise-based
292
+ functions:
132
293
133
294
``` php
134
295
$promise = React\Async\coroutine(function () {
0 commit comments