diff --git a/src/Promise.php b/src/Promise.php index 7c6176a4..e651e729 100644 --- a/src/Promise.php +++ b/src/Promise.php @@ -11,6 +11,7 @@ class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface private $progressHandlers = []; private $requiredCancelRequests = 0; + private $cancelRequests = 0; public function __construct(callable $resolver, callable $canceller = null) { @@ -58,15 +59,13 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, * These assumptions are covered by the test suite, so if you ever feel like * refactoring this, go ahead, any alternative suggestions are welcome! * - * @param Promise $parent + * @param self $parent * @return callable */ private static function parentCancellerFunction(self &$parent) { return function () use (&$parent) { - --$parent->requiredCancelRequests; - - if ($parent->requiredCancelRequests <= 0) { + if (++$parent->cancelRequests >= $parent->requiredCancelRequests) { $parent->cancel(); } @@ -121,37 +120,14 @@ public function progress(callable $onProgress) public function cancel() { - $canceller = $this->canceller; - $this->canceller = null; - - $parentCanceller = null; - - if (null !== $this->result) { - // Go up the promise chain and reach the top most promise which is - // itself not following another promise - $root = $this->unwrap($this->result); - - // Return if the root promise is already resolved or a - // FulfilledPromise or RejectedPromise - if (!$root instanceof self || null !== $root->result) { - return; - } - - $root->requiredCancelRequests--; - - if ($root->requiredCancelRequests <= 0) { - $parentCanceller = [$root, 'cancel']; - } + if (null === $this->canceller || null !== $this->result) { + return; } - if (null !== $canceller) { - $this->call($canceller); - } + $canceller = $this->canceller; + $this->canceller = null; - // For BC, we call the parent canceller after our own canceller - if ($parentCanceller) { - $parentCanceller(); - } + $this->call($canceller); } private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) @@ -200,16 +176,11 @@ private function settle(ExtendedPromiseInterface $promise) ); } - if ($promise instanceof self) { - $promise->requiredCancelRequests++; - } else { - $this->canceller = null; - } - $handlers = $this->handlers; $this->progressHandlers = $this->handlers = []; $this->result = $promise; + $this->canceller = null; foreach ($handlers as $handler) { $handler($promise); diff --git a/tests/PromiseTest/CancelTestTrait.php b/tests/PromiseTest/CancelTestTrait.php index aafe0688..2baab024 100644 --- a/tests/PromiseTest/CancelTestTrait.php +++ b/tests/PromiseTest/CancelTestTrait.php @@ -243,72 +243,4 @@ public function cancelShouldAlwaysTriggerCancellerWhenCalledOnRootPromise() $adapter->promise()->cancel(); } - - /** @test */ - public function cancelShouldTriggerCancellerWhenFollowerCancels() - { - $adapter1 = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $root = $adapter1->promise(); - - $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $follower = $adapter2->promise(); - $adapter2->resolve($root); - - $follower->cancel(); - } - - /** @test */ - public function cancelShouldNotTriggerCancellerWhenCancellingOnlyOneFollower() - { - $adapter1 = $this->getPromiseTestAdapter($this->expectCallableNever()); - - $root = $adapter1->promise(); - - $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $follower1 = $adapter2->promise(); - $adapter2->resolve($root); - - $adapter3 = $this->getPromiseTestAdapter($this->expectCallableNever()); - $adapter3->resolve($root); - - $follower1->cancel(); - } - - /** @test */ - public function cancelCalledOnFollowerShouldOnlyCancelWhenAllChildrenAndFollowerCancelled() - { - $adapter1 = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $root = $adapter1->promise(); - - $child = $root->then(); - - $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $follower = $adapter2->promise(); - $adapter2->resolve($root); - - $follower->cancel(); - $child->cancel(); - } - - /** @test */ - public function cancelShouldNotTriggerCancellerWhenCancellingFollowerButNotChildren() - { - $adapter1 = $this->getPromiseTestAdapter($this->expectCallableNever()); - - $root = $adapter1->promise(); - - $root->then(); - - $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $follower = $adapter2->promise(); - $adapter2->resolve($root); - - $follower->cancel(); - } }