/** * Start crawling. * * @param int $chunk * * @return \GuzzleHttp\Promise\PromiseInterface */ public function start($chunk = 5) { $this->dispatchStart(); // We need to use a double loop of generators here, because // if $chunk is greater than the number of items in the queue, // the requestWorkerFn exits the generator loop before any new // requests can be added by processing and cannot be restarted. // The outer generator ($gen) restarts the processing in that case. $gen = function () use($chunk) { while ($this->queue->count() > 0) { $inner = new EachPromise($this->getRequestWorkerFn(), ['concurrency' => $chunk]); (yield $inner->promise()); } }; $outer = new EachPromise($gen(), ['concurrency' => 1]); $finish = function ($results) { $this->dispatchFinish(); return $results; }; return $outer->promise()->then($finish, $finish); }
/** * @return \GuzzleHttp\Promise\PromiseInterface */ public function promise() { return $this->each->promise(); }
public function testDoesNotCallNextOnIteratorUntilNeededWhenAsync() { $firstPromise = new Promise(); $pending = [$firstPromise]; $values = [$firstPromise]; $results = []; $remaining = 9; $iter = function () use(&$values) { while ($value = array_pop($values)) { (yield $value); } }; $each = new EachPromise($iter(), ['concurrency' => 1, 'fulfilled' => function ($r) use(&$results, &$values, &$remaining, &$pending) { $results[] = $r; if ($remaining-- > 0) { $pending[] = $values[] = new Promise(); } }]); $i = 0; $each->promise(); while ($promise = array_pop($pending)) { $promise->resolve($i++); P\queue()->run(); } $this->assertEquals(range(0, 9), $results); }
public function testRejectsAggregateWhenNextThrows() { $iter = function () { (yield 'a'); throw new \Exception('Failure'); }; $each = new EachPromise($iter()); $p = $each->promise(); $e = null; $received = null; $p->then(null, function ($reason) use(&$e) { $e = $reason; }); P\queue()->run(); $this->assertInstanceOf('Exception', $e); $this->assertEquals('Failure', $e->getMessage()); }
public function testMutexPreventsGeneratorRecursion() { $results = $promises = []; for ($i = 0; $i < 20; $i++) { $p = $this->createSelfResolvingPromise($i); $pending[] = $p; $promises[] = $p; } $iter = function () use(&$promises, &$pending) { foreach ($promises as $promise) { // Resolve a promises, which will trigger the then() function, // which would cause the EachPromise to try to add more // promises to the queue. Without a lock, this would trigger // a "Cannot resume an already running generator" fatal error. if ($p = array_pop($pending)) { $p->wait(); } (yield $promise); } }; $each = new EachPromise($iter(), ['concurrency' => 5, 'fulfilled' => function ($r) use(&$results, &$pending) { $results[] = $r; }]); $each->promise()->wait(); $this->assertCount(20, $results); }