1- # Async
1+ # Async Utilities
22
33[ ![ CI status] ( https://github.com/reactphp/async/workflows/CI/badge.svg )] ( https://github.com/reactphp/async/actions )
44
@@ -16,6 +16,7 @@ an event loop, it can be used with this library.
1616** Table of Contents**
1717
1818* [ Usage] ( #usage )
19+ * [ async()] ( #async )
1920 * [ await()] ( #await )
2021 * [ coroutine()] ( #coroutine )
2122 * [ parallel()] ( #parallel )
@@ -53,6 +54,146 @@ use React\Async;
5354Async\await(…);
5455```
5556
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+
56197### await()
57198
58199The ` await(PromiseInterface $promise): mixed ` function can be used to
@@ -63,8 +204,27 @@ $result = React\Async\await($promise);
63204```
64205
65206This 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.
68228
69229Once the promise is fulfilled, this function will return whatever the promise
70230resolved to.
@@ -125,10 +285,11 @@ when the promise is fulfilled. The `yield` statement returns whatever the
125285promise is fulfilled with. If the promise is rejected, it will throw an
126286` Exception ` or ` Throwable ` .
127287
128- The ` coroutine() ` function will always return a Proimise which will be
288+ The ` coroutine() ` function will always return a Promise which will be
129289fulfilled with whatever your ` $function ` returns. Likewise, it will return
130290a 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:
132293
133294``` php
134295$promise = React\Async\coroutine(function () {
0 commit comments