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

+75-78
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

+2-1
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

+41-46
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)