Skip to content

Commit b2dc8ab

Browse files
Merge pull request #2 from TheIrritainer/master
Add support for failing promises in promise.all
2 parents 457b34c + a5ee2a5 commit b2dc8ab

File tree

5 files changed

+132
-9
lines changed

5 files changed

+132
-9
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
clover.xml
44
coveralls-upload.json
55
phpunit.xml
6-
vendor/
6+
vendor/cghooks.lock
7+
vendor/

lib/ExtSwoolePromise.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,13 @@ public function then(?callable $onFulfilled = null, ?callable $onRejected = null
101101
*/
102102
public static function all(iterable $promises): PromiseInterface
103103
{
104-
return self::create(function (callable $resolve) use ($promises) {
105-
$ticks = count($promises);
106-
$channel = new Channel($ticks);
107-
$result = new ArrayCollection();
108-
$key = 0;
104+
return self::create(function (callable $resolve, callable $reject) use ($promises) {
105+
$ticks = count($promises);
106+
107+
$firstError = null;
108+
$channel = new Channel($ticks);
109+
$result = new ArrayCollection();
110+
$key = 0;
109111
foreach ($promises as $promise) {
110112
if (!$promise instanceof ExtSwoolePromise) {
111113
$channel->close();
@@ -117,13 +119,24 @@ public static function all(iterable $promises): PromiseInterface
117119
$result->set($key, $value);
118120
$channel->push(true);
119121
return $value;
122+
}, function ($error) use ($channel, &$firstError) {
123+
$channel->push(true);
124+
if ($firstError === null) {
125+
$firstError = $error;
126+
}
120127
});
121128
$key++;
122129
}
123130
while ($ticks--) {
124131
$channel->pop();
125132
}
126133
$channel->close();
134+
135+
if ($firstError !== null) {
136+
$reject($firstError);
137+
return;
138+
}
139+
127140
$resolve($result);
128141
});
129142
}

lib/Promise.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,34 @@ public function then(?callable $onFulfilled = null, ?callable $onRejected = null
7676
*/
7777
public static function all(iterable $promises): PromiseInterface
7878
{
79-
return self::create(function (callable $resolve) use ($promises) {
80-
$result = new ArrayCollection();
81-
$key = 0;
79+
return self::create(function (callable $resolve, callable $reject) use ($promises) {
80+
$result = new ArrayCollection();
81+
$key = 0;
82+
$firstError = null;
83+
8284
foreach ($promises as $promise) {
8385
if (!$promise instanceof Promise) {
8486
throw new Exception\RuntimeException('Supported only Streamcommon\Promise\Promise instance');
8587
}
8688
$promise->then(function ($value) use ($key, $result) {
8789
$result->set($key, $value);
8890
return $value;
91+
}, function ($error) use (&$firstError) {
92+
if ($firstError !== null) {
93+
return;
94+
}
95+
96+
$firstError = $error;
8997
});
9098
$promise->wait();
9199
$key++;
92100
}
101+
102+
if ($firstError !== null) {
103+
$reject($firstError);
104+
return;
105+
}
106+
93107
$resolve($result);
94108
});
95109
}

test/ExtSwoolePromiseTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,51 @@ public function testPromiseAllException(): void
246246
});
247247
}
248248

249+
250+
/**
251+
* Test promise all with failures
252+
*
253+
* @return void
254+
*/
255+
public function testPromiseAnyFailure(): void
256+
{
257+
$promise1 = Promise::create(function (callable $resolve) {
258+
$resolve(41);
259+
});
260+
$promise2 = ExtSwoolePromise::create(function (callable $resolve, callable $reject) {
261+
$reject('A incidental error has occurred');
262+
});
263+
264+
/** @var ExtSwoolePromise $promise */
265+
$promise = ExtSwoolePromise::all([$promise1, $promise2]);
266+
$promise->then(null, function ($value) {
267+
$this->assertEquals('A incidental error has occurred', $value);
268+
});
269+
}
270+
271+
/**
272+
* Test promise all first error received is the error returned
273+
*
274+
* @return void
275+
*/
276+
public function testPromiseAllMultipleErrorsWillStillOnlyReturnFirstFailure()
277+
{
278+
$promise1 = ExtSwoolePromise::create(function (callable $resolve, callable $reject) {
279+
$reject(new RuntimeException('some failing message'));
280+
});
281+
282+
$promise2 = ExtSwoolePromise::create(function (callable $resolve, callable $reject) {
283+
$reject(new RuntimeException('this message is also failing but should not appear'));
284+
});
285+
286+
/** @var ExtSwoolePromise $promise */
287+
$promise = ExtSwoolePromise::all([$promise1, $promise2]);
288+
$promise->then(null, function ($value) {
289+
$this->assertEquals('some failing message', $value);
290+
});
291+
}
292+
293+
249294
/**
250295
* Test promise catch
251296
*

test/PromiseTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,56 @@ public function testPromiseAllException(): void
254254
$promise->wait();
255255
}
256256

257+
/**
258+
* Test promise all with failures
259+
*
260+
* @return void
261+
*/
262+
public function testPromiseAnyFailure(): void
263+
{
264+
$promise1 = Promise::create(function (callable $resolve) {
265+
$resolve(41);
266+
});
267+
$promise2 = Promise::create(function (callable $resolve, callable $reject) {
268+
$reject('A incidental error has occurred');
269+
});
270+
271+
/** @var Promise $promise */
272+
$promise = Promise::all([$promise1, $promise2]);
273+
$promise->then(null, function ($value) {
274+
$this->assertEquals('A incidental error has occurred', $value);
275+
});
276+
277+
$promise->wait();
278+
}
279+
280+
/**
281+
* Test promise all first error received is the error returned
282+
*
283+
* @return void
284+
*/
285+
public function testPromiseAllMultipleErrorsWillStillOnlyReturnFirstFailure()
286+
{
287+
$promise1 = Promise::create(function (callable $resolve, callable $reject) {
288+
$reject(new RuntimeException('some failing message'));
289+
});
290+
291+
$promise2 = Promise::create(function (callable $resolve, callable $reject) {
292+
$reject(new RuntimeException('this message is also failing but should not appear'));
293+
});
294+
295+
/** @var Promise $promise */
296+
$promise = Promise::all([$promise1, $promise2]);
297+
$promise->then(null, function ($value) {
298+
$this->assertEquals('some failing message', $value);
299+
});
300+
301+
$promise->wait();
302+
}
303+
304+
305+
306+
257307
/**
258308
* Test promise catch
259309
*

0 commit comments

Comments
 (0)