Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ There are many implementations of the concept of a `Promise`. This library aims

***

<a href="https://github.com/PhpGt/Promise/actions" target="_blank">
<a href="https://github.com/phpgt/Promise/actions" target="_blank">
<img src="https://badge.status.php.gt/promise-build.svg" alt="Build status" />
</a>
<a href="https://app.codacy.com/gh/PhpGt/Promise" target="_blank">
Expand All @@ -18,14 +18,14 @@ There are many implementations of the concept of a `Promise`. This library aims
<img src="https://badge.status.php.gt/promise-version.svg" alt="Current version" />
</a>
<a href="http://www.php.gt/promise" target="_blank">
<img src="https://badge.status.php.gt/promise-docs.svg" alt="PHP.Gt/Promise documentation" />
<img src="https://badge.status.php.gt/promise-docs.svg" alt="PHP.GT/Promise documentation" />
</a>

In computer science, a `Promise` is a mechanism that provides a simple and direct relationship between procedural code and asynchronous callbacks. Functions within procedural languages, like plain old PHP, have two ways they can affect your program's flow: either by returning values or throwing exceptions.

When working with functions that execute asynchronously, we can't return values because they might not be ready yet, and we can't throw exceptions because that's a procedural concept (where should we catch them?). That's where promises come in: instead of returning a value or throwing an exception, your functions can return a `Promise`, which is an object that can be _fulfilled_ with a value, or _rejected_ with an exception, but not necessarily at the point that they are returned.

With this concept, the actual work that calculates or loads the value required by your code can be _deferred_ to a task that executes asynchronously. Behind the scenes of PHP.Gt/Promise is a `Deferred` class that is used for exactly this.
With this concept, the actual work that calculates or loads the value required by your code can be _deferred_ to a task that executes asynchronously. Behind the scenes of PHP.GT/Promise is a `Deferred` class that is used for exactly this.

Example usage
-------------
Expand Down Expand Up @@ -120,7 +120,7 @@ Event loop

In order for this Promise library to be useful, some code has got to act as an event loop to call the deferred processes. This could be a simple while loop, but for real world tasks a more comprehensive loop system should be used.

The implementation of a Promise-based architecture is complex enough on its own, so the responsibility of an event loop library is maintained separately in [PHP.Gt/Async][gt-async].
The implementation of a Promise-based architecture is complex enough on its own, so the responsibility of an event loop library is maintained separately in [PHP.GT/Async][gt-async].

Special thanks
--------------
Expand Down
1 change: 0 additions & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
colors="true"
cacheDirectory="test/phpunit/.phpunit.cache"
bootstrap="vendor/autoload.php"
displayDetailsOnTestsThatTriggerDeprecations="true"
>
<coverage />

Expand Down
89 changes: 89 additions & 0 deletions src/ExecutesPromiseChain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
namespace Gt\Promise;

use Gt\Promise\Chain\CatchChain;
use Gt\Promise\Chain\Chainable;
use Gt\Promise\Chain\ChainFunctionTypeError;
use Gt\Promise\Chain\FinallyChain;
use Gt\Promise\Chain\ThenChain;

trait ExecutesPromiseChain {
private function complete():void {
usort($this->chain, $this->sortChainItems(...));

while($this->getState() !== PromiseState::PENDING) {
$chainItem = $this->getNextChainItem();
if(!$chainItem) {
break;
}

if($this->shouldSkipResolution($chainItem)) {
continue;
}

if($chainItem instanceof ThenChain) {
$this->executeThen($chainItem);
}
elseif($chainItem instanceof FinallyChain) {
$this->executeFinally($chainItem);
}
elseif($chainItem instanceof CatchChain) {
$this->executeCatch($chainItem);
}
}

$this->throwUnhandledRejection();
}

private function shouldSkipResolution(Chainable $chainItem):bool {
if($chainItem instanceof ThenChain || $chainItem instanceof FinallyChain) {
try {
if($this->resolvedValueSet && isset($this->resolvedValue)) {
$chainItem->checkResolutionCallbackType($this->resolvedValue);
}
}
catch(ChainFunctionTypeError) {
return true;
}
}
elseif($chainItem instanceof CatchChain) {
try {
if(isset($this->rejectedReason)) {
$chainItem->checkRejectionCallbackType($this->rejectedReason);
}
}
catch(ChainFunctionTypeError) {
return true;
}
}
return false;
}

private function executeThen(ThenChain $chainItem):void {
if($this->handleThen($chainItem)) {
$this->emptyChain();
}
}

private function executeFinally(FinallyChain $chainItem):void {
if($this->handleFinally($chainItem)) {
$this->emptyChain();
}
}

private function executeCatch(CatchChain $chainItem):void {
if($handled = $this->handleCatch($chainItem)) {
array_push($this->handledRejections, $handled);
}
}

private function sortChainItems(Chainable $a, Chainable $b):int {
if($a instanceof FinallyChain && !($b instanceof FinallyChain)) {
return 1;
}
if($b instanceof FinallyChain && !($a instanceof FinallyChain)) {
return -1;
}
return 0;
}
}
Loading