Skip to content

Commit

Permalink
Merge pull request #2 from TheIrritainer/master
Browse files Browse the repository at this point in the history
Add support for failing promises in promise.all
  • Loading branch information
aleksandr-yulin authored Jun 28, 2020
2 parents 457b34c + a5ee2a5 commit b2dc8ab
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
clover.xml
coveralls-upload.json
phpunit.xml
vendor/
vendor/cghooks.lock
vendor/
23 changes: 18 additions & 5 deletions lib/ExtSwoolePromise.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,13 @@ public function then(?callable $onFulfilled = null, ?callable $onRejected = null
*/
public static function all(iterable $promises): PromiseInterface
{
return self::create(function (callable $resolve) use ($promises) {
$ticks = count($promises);
$channel = new Channel($ticks);
$result = new ArrayCollection();
$key = 0;
return self::create(function (callable $resolve, callable $reject) use ($promises) {
$ticks = count($promises);

$firstError = null;
$channel = new Channel($ticks);
$result = new ArrayCollection();
$key = 0;
foreach ($promises as $promise) {
if (!$promise instanceof ExtSwoolePromise) {
$channel->close();
Expand All @@ -117,13 +119,24 @@ public static function all(iterable $promises): PromiseInterface
$result->set($key, $value);
$channel->push(true);
return $value;
}, function ($error) use ($channel, &$firstError) {
$channel->push(true);
if ($firstError === null) {
$firstError = $error;
}
});
$key++;
}
while ($ticks--) {
$channel->pop();
}
$channel->close();

if ($firstError !== null) {
$reject($firstError);
return;
}

$resolve($result);
});
}
Expand Down
20 changes: 17 additions & 3 deletions lib/Promise.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,34 @@ public function then(?callable $onFulfilled = null, ?callable $onRejected = null
*/
public static function all(iterable $promises): PromiseInterface
{
return self::create(function (callable $resolve) use ($promises) {
$result = new ArrayCollection();
$key = 0;
return self::create(function (callable $resolve, callable $reject) use ($promises) {
$result = new ArrayCollection();
$key = 0;
$firstError = null;

foreach ($promises as $promise) {
if (!$promise instanceof Promise) {
throw new Exception\RuntimeException('Supported only Streamcommon\Promise\Promise instance');
}
$promise->then(function ($value) use ($key, $result) {
$result->set($key, $value);
return $value;
}, function ($error) use (&$firstError) {
if ($firstError !== null) {
return;
}

$firstError = $error;
});
$promise->wait();
$key++;
}

if ($firstError !== null) {
$reject($firstError);
return;
}

$resolve($result);
});
}
Expand Down
45 changes: 45 additions & 0 deletions test/ExtSwoolePromiseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,51 @@ public function testPromiseAllException(): void
});
}


/**
* Test promise all with failures
*
* @return void
*/
public function testPromiseAnyFailure(): void
{
$promise1 = Promise::create(function (callable $resolve) {
$resolve(41);
});
$promise2 = ExtSwoolePromise::create(function (callable $resolve, callable $reject) {
$reject('A incidental error has occurred');
});

/** @var ExtSwoolePromise $promise */
$promise = ExtSwoolePromise::all([$promise1, $promise2]);
$promise->then(null, function ($value) {
$this->assertEquals('A incidental error has occurred', $value);
});
}

/**
* Test promise all first error received is the error returned
*
* @return void
*/
public function testPromiseAllMultipleErrorsWillStillOnlyReturnFirstFailure()
{
$promise1 = ExtSwoolePromise::create(function (callable $resolve, callable $reject) {
$reject(new RuntimeException('some failing message'));
});

$promise2 = ExtSwoolePromise::create(function (callable $resolve, callable $reject) {
$reject(new RuntimeException('this message is also failing but should not appear'));
});

/** @var ExtSwoolePromise $promise */
$promise = ExtSwoolePromise::all([$promise1, $promise2]);
$promise->then(null, function ($value) {
$this->assertEquals('some failing message', $value);
});
}


/**
* Test promise catch
*
Expand Down
50 changes: 50 additions & 0 deletions test/PromiseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,56 @@ public function testPromiseAllException(): void
$promise->wait();
}

/**
* Test promise all with failures
*
* @return void
*/
public function testPromiseAnyFailure(): void
{
$promise1 = Promise::create(function (callable $resolve) {
$resolve(41);
});
$promise2 = Promise::create(function (callable $resolve, callable $reject) {
$reject('A incidental error has occurred');
});

/** @var Promise $promise */
$promise = Promise::all([$promise1, $promise2]);
$promise->then(null, function ($value) {
$this->assertEquals('A incidental error has occurred', $value);
});

$promise->wait();
}

/**
* Test promise all first error received is the error returned
*
* @return void
*/
public function testPromiseAllMultipleErrorsWillStillOnlyReturnFirstFailure()
{
$promise1 = Promise::create(function (callable $resolve, callable $reject) {
$reject(new RuntimeException('some failing message'));
});

$promise2 = Promise::create(function (callable $resolve, callable $reject) {
$reject(new RuntimeException('this message is also failing but should not appear'));
});

/** @var Promise $promise */
$promise = Promise::all([$promise1, $promise2]);
$promise->then(null, function ($value) {
$this->assertEquals('some failing message', $value);
});

$promise->wait();
}




/**
* Test promise catch
*
Expand Down

0 comments on commit b2dc8ab

Please sign in to comment.