11# Promises
22
3- > ⚠️ ** Documentation still under construction**
4- >
5- > You're seeing an early draft of the documentation that is still in the works.
6- > Give feedback to help us prioritize.
7- > We also welcome [ contributors] ( ../more/community.md ) to help out!
8-
9- * Avoid blocking ([ databases] ( ../integrations/database.md ) , [ filesystem] ( ../integrations/filesystem.md ) , etc.)
10- * Deferred execution
11- * Concurrent execution more efficient than [ multithreading] ( child-processes.md )
12- * Avoid blocking by moving blocking implementation to [ child process] ( child-processes.md )
13-
14- === "Promise-based"
15-
16- ```php
17- $app->get('/book/{id:\d+}', function (Psr\Http\Message\ServerRequestInterface $request) use ($db, $twig) {
18- return $db->query(
19- 'SELECT * FROM books WHERE ID=?',
20- [$request->getAttribute('id')]
21- )->then(function (array $row) use ($twig) {
22- $html = $twig->render('book.twig', $row);
23-
24- return new React\Http\Message\Response(
25- 200,
26- [
27- 'Content-Type' => 'text/html; charset=utf-8'
28- ],
29- $html
30- );
31- });
32- });
33- ```
3+ Promises are a core building block used in all our async APIs. They are
4+ especially useful if want to express a concurrent code flow. You can directly
5+ use their API for maximum performance or use Fibers or Coroutines as an easier
6+ way to work with async APIs.
7+
8+ ## Quickstart
9+
10+ Let's take a look at the most basic promise usage by using an
11+ [ async database] ( ../integrations/database.md ) integration with X:
12+
13+ ``` php title="public/index.php"
14+ <?php
3415
35- === "Synchronous (for comparision)"
16+ require __DIR__ . '/../vendor/autoload.php';
3617
37- ```php
38- $app->get('/book/{id:\d+}', function (Psr\Http\Message\ServerRequestInterface $request) use ($db, $twig) {
39- $row = $db->query(
40- 'SELECT * FROM books WHERE ID=?',
41- [$request->getAttribute('id')]
18+ $credentials = 'alice:secret@localhost/bookstore?idle=0.001';
19+ $db = (new React\MySQL\Factory($loop))->createLazyConnection($credentials);
20+
21+ $app = new FrameworkX\App();
22+
23+ $app->get('/book', function () use ($db) {
24+ return $db->query(
25+ 'SELECT COUNT(*) AS count FROM book'
26+ )->then(function (React\MySQL\QueryResult $result) {
27+ $data = "Found " . $result->resultRows[0]['count'] . " books\n";
28+ return new React\Http\Message\Response(
29+ 200,
30+ [],
31+ $data
4232 );
33+ });
34+ });
35+
36+ $app->run():
37+ ```
4338
44- $html = $twig->render('book.twig', $row);
39+ As you can see, using an async database adapter in X with its promise-based API
40+ is similar to using a normal, synchronous database adapter such as PDO. The
41+ major difference is how the ` $db->query() ` call returns a promise that we use a
42+ chained ` then() ` call on to get its fulfillment value.
4543
44+ ## Requirements
45+
46+ X provides support for promises out of the box, so there's nothing special you
47+ have to install. If you've used promises before, you'll find a common API for
48+ promises in PHP thanks to [ reactphp/promise] ( https://github.com/reactphp/promise ) .
49+ This works across all supported PHP versions.
50+
51+ ## Usage
52+
53+ If you've used promises before, you'll find that using promise-based APIs in X
54+ is pretty straightfowrard. The gist is that when you're working with an async
55+ API that returns a promise, you have to use a chained ` then() ` call on it in
56+ order to "await" its fulfillment value. This is best shown in a simple example:
57+
58+ ``` php title="public/index.php" hl_lines="11-13"
59+ <?php
60+
61+ require __DIR__ . '/../vendor/autoload.php';
62+
63+ $credentials = 'alice:secret@localhost/bookstore?idle=0.001';
64+ $db = (new React\MySQL\Factory($loop))->createLazyConnection($credentials);
65+
66+ $app = new FrameworkX\App();
67+
68+ $app->get('/book', function () use ($db) {
69+ return $db->query(
70+ 'SELECT COUNT(*) AS count FROM book'
71+ )->then(function (React\MySQL\QueryResult $result) {
72+ $data = "Found " . $result->resultRows[0]['count'] . " books\n";
4673 return new React\Http\Message\Response(
4774 200,
48- [
49- 'Content-Type' => 'text/html; charset=utf-8'
50- ],
51- $html
75+ [],
76+ $data
5277 );
5378 });
54- ```
79+ });
80+
81+ $app->run():
82+ ```
83+
84+ Even in simple use cases such as above, promise-based APIs can take some time to
85+ get used to. At the same time, promise-based abstractions are one of the most
86+ efficient ways to express asynchronous APIs and as such are used throughout X
87+ and ReactPHP's ecosystem.
88+
89+ One of the most obvious consequences of using promises for async APIs is that it
90+ requires the calling side to know how to handle an async API.
91+
92+ This can be seen when breaking the above function up into a ` BookLookupController `
93+ and a ` BookRepository ` . Let's start by creating the ` BookRepository ` which consumes
94+ our async database API:
95+
96+ ``` php title="src/BookRepository.php" hl_lines="18-19 21-24"
97+ <?php
5598
56- In this example, we assume an [ async database] ( ../integrations/database.md )
57- adapter that returns a promise which * fulfills* with some data instead of
58- directly returning data.
99+ namespace Acme\Todo;
100+
101+ use React\MySQL\ConnectionInterface;
102+ use React\MySQL\QueryResult;
103+ use React\Promise\PromiseInterface;
104+
105+ class BookRepository
106+ {
107+ private $db;
108+
109+ public function __construct(ConnectionInterface $db)
110+ {
111+ $this->db = $db;
112+ }
113+
114+ /** @return PromiseInterface<?Book> **/
115+ public function findBook(string $isbn): PromiseInterface
116+ {
117+ return $this->db->query(
118+ 'SELECT title FROM book WHERE isbn = ?',
119+ [$isbn]
120+ )->then(function (QueryResult $result) {
121+ if (count($result->resultRows) === 0) {
122+ return null;
123+ }
124+
125+ return new Book($result->resultRows[0]['title']);
126+ });
127+ }
128+ }
129+ ```
130+
131+ Likewise, the ` BookLookupController ` consumes the API of the ` BookRepository ` by again
132+ using its promise-based API:
133+
134+ ``` php title="src/BookLookupController.php" hl_lines="19-20 23"
135+ <?php
136+
137+ namespace Acme\Todo;
138+
139+ use Psr\Http\Message\ResponseInterface;
140+ use Psr\Http\Message\ServerRequestInterface;
141+ use React\Http\Message\Response;
142+ use React\Promise\PromiseInterface;
143+
144+ class BookLookupController
145+ {
146+ private $repository;
147+
148+ public function __construct(BookRepository $repository)
149+ {
150+ $this->repository = $repository;
151+ }
152+
153+ /** @return PromiseInterface<ResponseInterface > **/
154+ public function __invoke(ServerRequestInterface $request): PromiseInterface
155+ {
156+ $isbn = $request->getAttribute('isbn');
157+ return $this->repository->findBook($isbn)->then(function (?Book $book) {
158+ if ($book === null) {
159+ return new Response(
160+ 404,
161+ [],
162+ "Book not found\n"
163+ );
164+ }
165+
166+ $data = $book->title;
167+ return new Response(
168+ 200,
169+ [],
170+ $data
171+ );
172+ });
173+ }
174+ }
175+ ```
176+
177+ As we can see, both classes need to return a promise and the calling side in
178+ turn needs to handle this. This is all taken care of by X automatically when
179+ you use promises anywhere in your controller function.
180+
181+ See also [ async database APIs] ( ../integrations/database.md#recommended-class-structure )
182+ for more details.
183+
184+ ## FAQ
185+
186+ ### When to use promises?
187+
188+ As a rule of thumb, promise-based APIs are one of the most efficient ways to
189+ express asynchronous APIs and as such are used throughout X and ReactPHP's
190+ ecosystem. You can always use promises as a core building block for async APIs
191+ for maximum performance.
192+
193+ At the same time, using [ fibers] ( fibers.md ) and [ coroutines] ( coroutines.md ) is
194+ often much easier as it allows consuming async APIs in a way that resembles a
195+ synchronous code flow. Both build on top of promises, so there's a fair chance
196+ you'll end up using promises one way or another no matter what.
59197
60198The major feature is that this means that anything that takes some time will
61199no longer block the entire execution.
@@ -66,8 +204,7 @@ some kind of <abbrev title="Input/Output">I/O</abbrev>, such as
66204If you want to learn more about the promise API, see also
67205[ reactphp/promise] ( https://reactphp.org/promise/ ) .
68206
69- Admittedly, this example also showcases how async PHP can look slightly more
70- complicated than a normal, synchronous code structure.
71- Because we realize this API can be somewhat harder in some cases, we also
72- support [ coroutines] ( coroutines.md ) (and in upcoming PHP 8.1 will also support
73- [ fibers] ( fibers.md ) ).
207+ ### How do promises work?
208+
209+ If you want to learn more about the promise API, see also
210+ [ reactphp/promise] ( https://reactphp.org/promise/ ) .
0 commit comments