@@ -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,145 @@ 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+
106+ The ` async() ` function is specifically designed for cases where it is used
107+ as a callback (such as an event loop timer, event listener, or promise
108+ callback). For this reason, it returns a new function wrapping the given
109+ ` $function ` instead of directly invoking it and returning its value.
110+
111+ ``` php
112+ use function React\Async\async;
113+
114+ Loop::addTimer(1.0, async(function () { … }));
115+ $connection->on('close', async(function () { … }));
116+ $stream->on('data', async(function ($data) { … }));
117+ $promise->then(async(function (int $result) { … }));
118+ ```
119+
120+ You can invoke this wrapping function to invoke the given ` $function ` with
121+ any arguments given as-is. The function will always return a Promise which
122+ will be fulfilled with whatever your ` $function ` returns. Likewise, it will
123+ return a promise that will be rejected if you throw an ` Exception ` or
124+ ` Throwable ` from your ` $function ` . This allows you to easily create
125+ Promise-based functions:
126+
127+ ``` php
128+ $promise = React\Async\async(function (): int {
129+ $browser = new React\Http\Browser();
130+ $urls = [
131+ 'https://example.com/alice',
132+ 'https://example.com/bob'
133+ ];
134+
135+ $bytes = 0;
136+ foreach ($urls as $url) {
137+ $response = React\Async\await($browser->get($url));
138+ assert($response instanceof Psr\Http\Message\ResponseInterface);
139+ $bytes += $response->getBody()->getSize();
140+ }
141+ return $bytes;
142+ })();
143+
144+ $promise->then(function (int $bytes) {
145+ echo 'Total size: ' . $bytes . PHP_EOL;
146+ }, function (Exception $e) {
147+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
148+ });
149+ ```
150+
151+ The previous example uses [ ` await() ` ] ( #await ) inside a loop to highlight how
152+ this vastly simplifies consuming asynchronous operations. At the same time,
153+ this naive example does not leverage concurrent execution, as it will
154+ essentially "await" between each operation. In order to take advantage of
155+ concurrent execution within the given ` $function ` , you can "await" multiple
156+ promises by using a single [ ` await() ` ] ( #await ) together with Promise-based
157+ primitives like this:
158+
159+ ``` php
160+ $promise = React\Async\async(function (): int {
161+ $browser = new React\Http\Browser();
162+ $urls = [
163+ 'https://example.com/alice',
164+ 'https://example.com/bob'
165+ ];
166+
167+ $promises = [];
168+ foreach ($urls as $url) {
169+ $promises[] = $browser->get($url);
170+ }
171+
172+ try {
173+ $responses = React\Async\await(React\Promise\all($promises));
174+ } catch (Exception $e) {
175+ foreach ($promises as $promise) {
176+ $promise->cancel();
177+ }
178+ throw $e;
179+ }
180+
181+ $bytes = 0;
182+ foreach ($responses as $response) {
183+ assert($response instanceof Psr\Http\Message\ResponseInterface);
184+ $bytes += $response->getBody()->getSize();
185+ }
186+ return $bytes;
187+ })();
188+
189+ $promise->then(function (int $bytes) {
190+ echo 'Total size: ' . $bytes . PHP_EOL;
191+ }, function (Exception $e) {
192+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
193+ });
194+ ```
195+
56196### await()
57197
58198The ` await(PromiseInterface $promise): mixed ` function can be used to
@@ -63,8 +203,27 @@ $result = React\Async\await($promise);
63203```
64204
65205This 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.
206+ either fulfilled or rejected. While the promise is pending, this function
207+ can be considered * blocking* from the perspective of the calling code.
208+ You can avoid this blocking behavior by wrapping it in an [ ` async() ` function] ( #async )
209+ call. Everything inside this function will still be blocked, but everything
210+ outside this function can be executed asynchronously without blocking:
211+
212+ ``` php
213+ Loop::addTimer(0.5, React\Async\async(function() {
214+ echo 'a';
215+ React\async\await(React\Promise\Timer\sleep(1.0));
216+ echo 'c';
217+ }));
218+
219+ Loop::addTimer(1.0, fn() => echo 'b'));
220+
221+ // prints "a" at t=0.5s
222+ // prints "b" at t=1.0s
223+ // prints "c" at t=1.5s
224+ ```
225+
226+ See also the [ ` async() ` function] ( #async ) for more details.
68227
69228Once the promise is fulfilled, this function will return whatever the promise
70229resolved to.
@@ -125,10 +284,11 @@ when the promise is fulfilled. The `yield` statement returns whatever the
125284promise is fulfilled with. If the promise is rejected, it will throw an
126285` Exception ` or ` Throwable ` .
127286
128- The ` coroutine() ` function will always return a Proimise which will be
287+ The ` coroutine() ` function will always return a Promise which will be
129288fulfilled with whatever your ` $function ` returns. Likewise, it will return
130289a promise that will be rejected if you throw an ` Exception ` or ` Throwable `
131- from your ` $function ` . This allows you easily create Promise-based functions:
290+ from your ` $function ` . This allows you to easily create Promise-based
291+ functions:
132292
133293``` php
134294$promise = React\Async\coroutine(function () {
0 commit comments