Skip to content

Instantly share code, notes, and snippets.

@jsor
Last active July 31, 2017 12:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jsor/3a17c3da011a9f664b6d to your computer and use it in GitHub Desktop.
Save jsor/3a17c3da011a9f664b6d to your computer and use it in GitHub Desktop.
React/Promise coroutine
<?php
namespace React\Promise;
function coroutine(\Generator $generator)
{
return new Promise\Promise(function ($resolve, $reject, $notify) use ($generator) {
$continue = function ($previousResult = null) use (&$continue, $generator, $resolve, $reject, $notify) {
try {
$current = $generator->current();
} catch (\Exception $e) {
$reject($e);
return;
}
if (!$generator->valid()) {
$resolve($previousResult);
return;
}
Promise\resolve($current)
->then(
function ($result) use ($continue, $generator, $notify) {
$notify($result);
$generator->send($result);
$continue($result);
},
function ($error) use ($continue, $generator) {
$generator->throw($error);
$continue();
}
)
->done(null, $reject)
;
};
$continue();
});
}
<?php
namespace React\Promise;
class CoroutineTest extends \PHPUnit_Framework_TestCase
{
public function testResolvedValueEqualsFinalYield()
{
$gen = function () {
$a = (yield 21);
$b = (yield new FulfilledPromise(2));
yield ($a * $b);
};
$expected = 42;
$promise = coroutine($gen());
$mock = $this->getMock('React\\Stub\CallableStub');
$mock
->expects($this->once())
->method('__invoke')
->with($this->identicalTo($expected));
$promise->done($mock);
}
public function testPromiseRejectionsAreThrownIntoGenerator()
{
$deferred = new Deferred();
$deferred2 = new Deferred();
$gen = function () use ($deferred, $deferred2) {
yield $deferred->promise();
$a = (yield 21);
$b = 1;
try {
yield $deferred2->promise();
$this->fail('Code path should not be reached');
} catch (\Exception $e) {
$this->assertSame('test', $e->getMessage());
$b = 2;
}
yield ($a * $b);
};
$expected = 42;
$promise = coroutine($gen());
$mock = $this->getMock('React\\Stub\CallableStub');
$mock
->expects($this->once())
->method('__invoke')
->with($this->identicalTo($expected));
$promise->done($mock);
$deferred2->reject(new \Exception('test'));
$deferred->resolve();
}
/**
* @expectedException \Exception
* @expectedExceptionMessage When in the chronicle of wasted time
*/
public function testUncaughtGeneratorExceptionRejectsPromise()
{
$gen = function () {
yield;
throw new \Exception('When in the chronicle of wasted time');
yield;
};
$promise = coroutine($gen());
$promise->done();
}
}
<?php
use React\Promise;
$timeStart = microtime(true);
$gen = function () {
$a = (yield 21);
$b = (yield new Promise\FulfilledPromise(2));
yield ($a * $b);
};
for ($i = 0; $i < 1000; $i++) {
Promise\coroutine($gen())->done();
}
$timeEnd = microtime(true);
printf("%-25s %d iterations in %f seconds\n", 'coroutine()', 1000, $timeEnd - $timeStart);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment