Skip to content

Commit 6dcdf94

Browse files
authored
Merge pull request #7 from clue-labs/promise
Use Promise-based APIs instead of callbacks (continuation-passing style)
2 parents e0a97b1 + 067c2ff commit 6dcdf94

File tree

6 files changed

+225
-205
lines changed

6 files changed

+225
-205
lines changed

README.md

Lines changed: 75 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,17 @@
1-
# NOTE: This package is no longer maintained. Use [react/promise](https://github.com/reactphp/promise) instead!
2-
31
# Async
42

5-
Async utilities for [ReactPHP](https://reactphp.org/).
6-
7-
It is heavily influenced by [async.js](https://github.com/caolan/async).
8-
93
[![CI status](https://github.com/reactphp/async/workflows/CI/badge.svg)](https://github.com/reactphp/async/actions)
104

11-
This library allows you to manage async control flow. It provides a number of
12-
combinators for continuation-passing style (aka callbacks). Instead of nesting
13-
those callbacks, you can declare them as a list, which is resolved
14-
sequentially in an async manner.
5+
Async utilities for [ReactPHP](https://reactphp.org/).
156

7+
This library allows you to manage async control flow. It provides a number of
8+
combinators for [Promise](https://github.com/reactphp/promise)-based APIs.
9+
Instead of nesting or chaining promise callbacks, you can declare them as a
10+
list, which is resolved sequentially in an async manner.
1611
React/Async will not automagically change blocking code to be async. You need
1712
to have an actual event loop and non-blocking libraries interacting with that
18-
event loop for it to work. You can use `react/event-loop` for this, but you
19-
don't have to. As long as you have a callback-based API that runs in an event
20-
loop, it can be used with this library.
21-
22-
*You must be running inside an event loop for react/async to make any sense
23-
whatsoever!*
13+
event loop for it to work. As long as you have a Promise-based API that runs in
14+
an event loop, it can be used with this library.
2415

2516
**Table of Contents**
2617

@@ -62,112 +53,116 @@ Async\parallel(…);
6253

6354
### parallel()
6455

65-
The `parallel(array<callable> $tasks, ?callable $callback = null, ?callable $errback = null): void` function can be used
56+
The `parallel(array<callable():PromiseInterface<mixed,Exception>> $tasks): PromiseInterface<array<mixed>,Exception>` function can be used
6657
like this:
6758

6859
```php
6960
<?php
7061

7162
use React\EventLoop\Loop;
63+
use React\Promise\Promise;
7264

73-
React\Async\parallel(
74-
array(
75-
function ($callback, $errback) {
76-
Loop::addTimer(1, function () use ($callback) {
77-
$callback('Slept for a whole second');
65+
React\Async\parallel([
66+
function () {
67+
return new Promise(function ($resolve) {
68+
Loop::addTimer(1, function () use ($resolve) {
69+
$resolve('Slept for a whole second');
7870
});
79-
},
80-
function ($callback, $errback) {
81-
Loop::addTimer(1, function () use ($callback) {
82-
$callback('Slept for another whole second');
71+
});
72+
},
73+
function () {
74+
return new Promise(function ($resolve) {
75+
Loop::addTimer(1, function () use ($resolve) {
76+
$resolve('Slept for another whole second');
8377
});
84-
},
85-
function ($callback, $errback) {
86-
Loop::addTimer(1, function () use ($callback) {
87-
$callback('Slept for yet another whole second');
78+
});
79+
},
80+
function () {
81+
return new Promise(function ($resolve) {
82+
Loop::addTimer(1, function () use ($resolve) {
83+
$resolve('Slept for yet another whole second');
8884
});
89-
},
90-
),
91-
function (array $results) {
92-
foreach ($results as $result) {
93-
var_dump($result);
94-
}
85+
});
9586
},
96-
function (Exception $e) {
97-
throw $e;
87+
])->then(function (array $results) {
88+
foreach ($results as $result) {
89+
var_dump($result);
9890
}
99-
);
91+
}, function (Exception $e) {
92+
echo 'Error: ' . $e->getMessage() . PHP_EOL;
93+
});
10094
```
10195

10296
### series()
10397

104-
The `series(array<callable> $tasks, ?callable $callback = null, ?callable $errback = null): void` function can be used
98+
The `series(array<callable():PromiseInterface<mixed,Exception>> $tasks): PromiseInterface<array<mixed>,Exception>` function can be used
10599
like this:
106100

107101
```php
108102
<?php
109103

110104
use React\EventLoop\Loop;
105+
use React\Promise\Promise;
111106

112-
React\Async\series(
113-
array(
114-
function ($callback, $errback) {
115-
Loop::addTimer(1, function () use ($callback) {
116-
$callback('Slept for a whole second');
107+
React\Async\series([
108+
function () {
109+
return new Promise(function ($resolve) {
110+
Loop::addTimer(1, function () use ($resolve) {
111+
$resolve('Slept for a whole second');
117112
});
118-
},
119-
function ($callback, $errback) {
120-
Loop::addTimer(1, function () use ($callback) {
121-
$callback('Slept for another whole second');
113+
});
114+
},
115+
function () {
116+
return new Promise(function ($resolve) {
117+
Loop::addTimer(1, function () use ($resolve) {
118+
$resolve('Slept for another whole second');
122119
});
123-
},
124-
function ($callback, $errback) {
125-
Loop::addTimer(1, function () use ($callback) {
126-
$callback('Slept for yet another whole second');
120+
});
121+
},
122+
function () {
123+
return new Promise(function ($resolve) {
124+
Loop::addTimer(1, function () use ($resolve) {
125+
$resolve('Slept for yet another whole second');
127126
});
128-
},
129-
),
130-
function (array $results) {
131-
foreach ($results as $result) {
132-
var_dump($result);
133-
}
127+
});
134128
},
135-
function (Exception $e) {
136-
throw $e;
129+
])->then(function (array $results) {
130+
foreach ($results as $result) {
131+
var_dump($result);
137132
}
138-
);
133+
}, function (Exception $e) {
134+
echo 'Error: ' . $e->getMessage() . PHP_EOL;
135+
});
139136
```
140137

141138
### waterfall()
142139

143-
The `waterfall(array<callable> $tasks, ?callable $callback = null, ?callable $errback = null): void` function can be used
140+
The `waterfall(array<callable(mixed=):PromiseInterface<mixed,Exception>> $tasks): PromiseInterface<mixed,Exception>` function can be used
144141
like this:
145142

146143
```php
147144
<?php
148145

149146
use React\EventLoop\Loop;
147+
use React\Promise\Promise;
150148

151-
$addOne = function ($prev, $callback = null) {
152-
if (!$callback) {
153-
$callback = $prev;
154-
$prev = 0;
155-
}
156-
157-
Loop::addTimer(1, function () use ($prev, $callback) {
158-
$callback($prev + 1);
149+
$addOne = function ($prev = 0) {
150+
return new Promise(function ($resolve) use ($prev) {
151+
Loop::addTimer(1, function () use ($prev, $resolve) {
152+
$resolve($prev + 1);
153+
});
159154
});
160155
};
161156

162-
React\Async\waterfall(array(
163-
$addOne,
157+
React\Async\waterfall([
164158
$addOne,
165159
$addOne,
166-
function ($prev, $callback) use ($loop) {
167-
echo "Final result is $prev\n";
168-
$callback();
169-
},
170-
));
160+
$addOne
161+
])->then(function ($prev) {
162+
echo "Final result is $prev\n";
163+
}, function (Exception $e) {
164+
echo 'Error: ' . $e->getMessage() . PHP_EOL;
165+
});
171166
```
172167

173168
## Todo
@@ -210,3 +205,5 @@ $ php vendor/bin/phpunit
210205
## License
211206

212207
MIT, see [LICENSE file](LICENSE).
208+
209+
This project is heavily influenced by [async.js](https://github.com/caolan/async).

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
}
2727
],
2828
"require": {
29-
"php": ">=5.3.2"
29+
"php": ">=5.3.2",
30+
"react/promise": "^2.8 || ^1.2.1"
3031
},
3132
"require-dev": {
3233
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",

src/functions.php

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,32 @@
22

33
namespace React\Async;
44

5+
use React\Promise\Deferred;
6+
use React\Promise\PromiseInterface;
7+
58
/**
6-
* @param array<callable> $tasks
7-
* @param ?callable $callback
8-
* @param ?callable $errback
9-
* @return void
9+
* @param array<callable():PromiseInterface<mixed,Exception>> $tasks
10+
* @return PromiseInterface<array<mixed>,Exception>
1011
*/
11-
function parallel(array $tasks, $callback = null, $errback = null)
12+
function parallel(array $tasks)
1213
{
14+
$deferred = new Deferred();
1315
$results = array();
1416
$errors = array();
1517

16-
$done = function () use (&$results, &$errors, $callback, $errback) {
17-
if (!$callback) {
18-
return;
19-
}
20-
18+
$done = function () use (&$results, &$errors, $deferred) {
2119
if (count($errors)) {
22-
$errback(array_shift($errors));
20+
$deferred->reject(array_shift($errors));
2321
return;
2422
}
2523

26-
$callback($results);
24+
$deferred->resolve($results);
2725
};
2826

2927
$numTasks = count($tasks);
3028

3129
if (0 === $numTasks) {
3230
$done();
33-
return;
3431
}
3532

3633
$checkDone = function () use (&$results, &$errors, $numTasks, $done) {
@@ -50,18 +47,22 @@ function parallel(array $tasks, $callback = null, $errback = null)
5047
$checkDone();
5148
};
5249

53-
call_user_func($task, $taskCallback, $taskErrback);
50+
$promise = call_user_func($task);
51+
assert($promise instanceof PromiseInterface);
52+
53+
$promise->then($taskCallback, $taskErrback);
5454
}
55+
56+
return $deferred->promise();
5557
}
5658

5759
/**
58-
* @param array<callable> $tasks
59-
* @param ?callable $callback
60-
* @param ?callable $errback
61-
* @return void
60+
* @param array<callable():PromiseInterface<mixed,Exception>> $tasks
61+
* @return PromiseInterface<array<mixed>,Exception>
6262
*/
63-
function series(array $tasks, $callback = null, $errback = null)
63+
function series(array $tasks)
6464
{
65+
$deferred = new Deferred();
6566
$results = array();
6667

6768
/** @var callable():void $next */
@@ -70,53 +71,47 @@ function series(array $tasks, $callback = null, $errback = null)
7071
$next();
7172
};
7273

73-
$done = function () use (&$results, $callback) {
74-
if ($callback) {
75-
call_user_func($callback, $results);
76-
}
77-
};
78-
79-
$next = function () use (&$tasks, $taskCallback, $errback, $done) {
74+
$next = function () use (&$tasks, $taskCallback, $deferred, &$results) {
8075
if (0 === count($tasks)) {
81-
$done();
76+
$deferred->resolve($results);
8277
return;
8378
}
8479

8580
$task = array_shift($tasks);
86-
call_user_func($task, $taskCallback, $errback);
81+
$promise = call_user_func($task);
82+
assert($promise instanceof PromiseInterface);
83+
84+
$promise->then($taskCallback, array($deferred, 'reject'));
8785
};
8886

8987
$next();
88+
89+
return $deferred->promise();
9090
}
9191

9292
/**
93-
* @param array<callable> $tasks
94-
* @param ?callable $callback
95-
* @param ?callable $errback
96-
* @return void
93+
* @param array<callable(mixed=):PromiseInterface<mixed,Exception>> $tasks
94+
* @return PromiseInterface<mixed,Exception>
9795
*/
98-
function waterfall(array $tasks, $callback = null, $errback = null)
96+
function waterfall(array $tasks)
9997
{
100-
$taskCallback = function () use (&$next) {
101-
call_user_func_array($next, func_get_args());
102-
};
98+
$deferred = new Deferred();
10399

104-
$done = function () use ($callback) {
105-
if ($callback) {
106-
call_user_func_array($callback, func_get_args());
107-
}
108-
};
109-
110-
$next = function () use (&$tasks, $taskCallback, $errback, $done) {
100+
/** @var callable $next */
101+
$next = function ($value = null) use (&$tasks, &$next, $deferred) {
111102
if (0 === count($tasks)) {
112-
call_user_func_array($done, func_get_args());
103+
$deferred->resolve($value);
113104
return;
114105
}
115106

116107
$task = array_shift($tasks);
117-
$args = array_merge(func_get_args(), array($taskCallback, $errback));
118-
call_user_func_array($task, $args);
108+
$promise = call_user_func_array($task, func_get_args());
109+
assert($promise instanceof PromiseInterface);
110+
111+
$promise->then($next, array($deferred, 'reject'));
119112
};
120113

121114
$next();
115+
116+
return $deferred->promise();
122117
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy