public function testClearsReferencesWhenResolved() { $called = false; $a = new Promise(function () use(&$a, &$called) { $a->resolve('a'); $called = true; }); $each = new EachPromise([$a], ['concurrency' => function () { return 1; }, 'fulfilled' => function () { }, 'rejected' => function () { }]); $each->promise()->wait(); $this->assertNull($this->readAttribute($each, 'onFulfilled')); $this->assertNull($this->readAttribute($each, 'onRejected')); $this->assertNull($this->readAttribute($each, 'iterable')); $this->assertNull($this->readAttribute($each, 'pending')); $this->assertNull($this->readAttribute($each, 'concurrency')); $this->assertTrue($called); }
public function __invoke(RequestInterface $request, array $options) { $fn = $this->nextHandler; $throttleId = isset($options['throttle_id']) ? $options['throttle_id'] : null; $limit = isset($options['throttle_limit']) ? $options['throttle_limit'] : null; if (!$throttleId || !$limit) { // Request is not throttled; just ignore it. return $fn($request, $options); } if (!isset($this->running[$throttleId])) { $this->running[$throttleId] = 0; $this->queue[$throttleId] = []; } $promise = new Promise([\GuzzleHttp\Promise\queue(), 'run']); if ($this->running[$throttleId] + 1 <= $limit) { // Queue has enough space; run this request and watch for queue size. ++$this->running[$throttleId]; return $fn($request, $options)->then($this->getQueuePopper($throttleId, true), $this->getQueuePopper($throttleId, false)); } // The queue is full; delay the request, and don't forget to also pop the queue. $this->queue[$throttleId][] = function () use($request, $options, $fn, $throttleId, $promise) { $promise->resolve($fn($request, $options)->then($this->getQueuePopper($throttleId, true), $this->getQueuePopper($throttleId, false))); }; return $promise; }
/** * Add optional promise to process options * * @param Process $process * @return Promise */ private function addPromise(Process $process) { /** @var Promise $promise */ $promise = new Promise(function () use($process, &$promise) { $process->wait(); $promise->resolve($process); }); $process->setOptions([ProcessQueue::PROMISE_KEY => $promise]); return $promise; }
/** * @param Route $route * @return Promise|PromiseInterface * @throws \LogicException * @throws \InvalidArgumentException */ public function request(Route $route) { $request = $this->prepareRequest($this->gitter->token, $route); $this->logRequest($request); $responsePromise = $this->client->sendAsync($request, $this->options); $promise = new Promise(function ($unwrap = true) use($responsePromise) { $responsePromise->wait($unwrap); }); $responsePromise->then(function (ResponseInterface $response) use($promise) { $this->logResponse($response); $promise->resolve(json((string) $response->getBody(), true)); }); return $promise; }
/** * @param \SplFileInfo|string $cwd * @return PromiseInterface */ public function enqueue($cwd = null) { $process = $this->factory->make($cwd); /** @var Promise $promise */ $promise = new Promise(function () use($process, &$promise) { if ($process->isStarted()) { $process->wait(); } $process->isSuccessful() ? $promise->resolve($process) : $promise->reject($process); }); $this->queue->add($process); $process->setOptions([ProcessQueue::PROMISE_KEY => $promise]); return $promise; }
public function run(callable $callback, $time) { $promise = new Promise(); $loop = Runner::getLoop(); $loop->addTimer($time, function () use($promise, $callback) { if ($promise->getState() === PromiseInterface::PENDING) { try { $promise->resolve($callback()); } catch (\Exception $e) { $promise->reject($e); } } }); return $promise; }
public function __invoke(RequestInterface $request, array $options) { $artaxRequest = $this->convertRequest($request, $options); $artaxResponsePromise = $this->artaxClient->request($artaxRequest); $guzzleResponsePromise = new Promise(function () use($artaxResponsePromise) { \Amp\wait($artaxResponsePromise); }); $artaxResponsePromise->when(function ($error = null, Response $artaxResponse = null) use($request, $options, $guzzleResponsePromise) { if ($error) { $guzzleResponsePromise->reject($error); } else { $response = $this->convertResponse($artaxResponse, $request, $options); $guzzleResponsePromise->resolve($response); } }); return $guzzleResponsePromise; }
/** * Magic method which intercepts async calls, finds the sequential version, and wraps it in a * {@see Promise} object. In order for this to happen, the called methods need to be in the * following format: `createAsync`, where `create` is the sequential method being wrapped. * * @param $methodName The name of the method being invoked. * @param $args The arguments to be passed to the sequential method. * * @throws \RuntimeException If method does not exist * * @return Promise */ public function __call($methodName, $args) { $e = function ($name) { return new \RuntimeException(sprintf('%s::%s is not defined', get_class($this), $name)); }; if (substr($methodName, -5) === 'Async') { $realMethod = substr($methodName, 0, -5); if (!method_exists($this, $realMethod)) { throw $e($realMethod); } $promise = new Promise(function () use(&$promise, $realMethod, $args) { $value = call_user_func_array([$this, $realMethod], $args); $promise->resolve($value); }); return $promise; } throw $e($methodName); }
public function start() { $promise = new Promise([$this, 'wait']); $this->process->start(function ($type, $buffer) use($promise) { if (Process::ERR === $type) { $this->logger->error($buffer); } else { $this->logger->debug($buffer); } if ($promise->getState() === Promise::PENDING) { if (Process::ERR === $type) { $promise->reject(false); } else { $promise->resolve(true); } } }); return $promise; }
/** * @param Psr7Request $request * @param array $options * * @return Promise\Promise */ public function __invoke(Psr7Request $request, array $options = []) { // Create and send a Guzzle 5 request $guzzlePromise = $this->client->send($this->createGuzzleRequest($request, $options)); $promise = new Promise\Promise(function () use($guzzlePromise) { try { $guzzlePromise->wait(); } catch (\Exception $e) { // The promise is already delivered when the exception is // thrown, so don't rethrow it. } }, [$guzzlePromise, 'cancel']); $guzzlePromise->then([$promise, 'resolve'], [$promise, 'reject']); return $promise->then(function (GuzzleResponse $response) { // Adapt the Guzzle 5 Future to a Guzzle 6 ResponsePromise. return $this->createPsr7Response($response); }, function (Exception $exception) { // Reject with information about the error. return new Promise\RejectedPromise($this->prepareErrorData($exception)); }); }
/** * @param array $criteria Criterias for querying geocoding * @return GuzzlePromise The promise for guzzle request * * @throws InvalidArgumentException */ public function lookup($criteria) { // TODO: Discuss criteria validation if (!is_array($criteria)) { throw new InvalidArgumentException('Criteria must be an array with keys and values'); } $lookup_promise = null; $lookup_promise = new GuzzlePromise(function () use($criteria, &$lookup_promise) { $this->client_interface->sendAsync(new Request("GET", sprintf(self::GEOCODEURL, $this->response_type, $this->arrayToQueryParams($criteria))))->then(function ($response) use($lookup_promise) { if ($response->getStatusCode() === 200) { $geo_location_response = json_decode($response->getBody()); if ($geo_location_response->status !== "OK") { $lookup_promise->reject(sprintf("Server responded with status %s", $geo_location_response->status)); } else { $lookup_promise->resolve(Factory::create('GoogleGeoCoder\\GeoCollection', $geo_location_response->results)); } } else { $lookup_promise->reject(sprintf("Server responded with code %s", $response->getStatusCode())); } }, function ($response) use($lookup_promise) { $lookup_promise->reject(sprintf("Server responded with failure")); })->wait(); }); return $lookup_promise; }
/** * @internal */ public function _handleFailure($reason) { unset($this->currentPromise); try { $nextYield = $this->generator->throw(exception_for($reason)); // The throw was caught, so keep iterating on the coroutine $this->nextCoroutine($nextYield); } catch (Exception $exception) { $this->result->reject($exception); } catch (Throwable $throwable) { $this->result->reject($throwable); } }
/** * @test */ public function it_rejects_deferred_with_runtime_exception_asynchronous_when_json_response_is_invalid() { $producerInAsyncMode = new HttpMessageProducer($this->guzzleClient, new NoOpMessageConverter(), null, true); $fetchDataQuery = new FetchSomething(['filter' => 'foo']); $psrResponse = $this->prophesize(ResponseInterface::class); $queryDeferred = new Deferred(); $producerInAsyncMode($fetchDataQuery, $queryDeferred); //Return invalid json $psrResponse->getBody()->willReturn('[{"data" => "bar"'); $this->responsePromise->resolve($psrResponse->reveal()); //Perform next tick, required to resolve the promise when we are in async mode $queue = \GuzzleHttp\Promise\queue(); $queue->run(); $rejectionReason = null; $queryDeferred->promise()->otherwise(function ($reason) use(&$rejectionReason) { $rejectionReason = $reason; }); $this->assertInstanceOf(RuntimeException::class, $rejectionReason); }
public function testOtherwiseIsSugarForRejections() { $p = new Promise(); $p->reject('foo'); $p->otherwise(function ($v) use(&$c) { $c = $v; }); P\queue()->run(); $this->assertEquals($c, 'foo'); }
public function testCoroutineOtherwiseIntegrationTest() { $a = new P\Promise(); $b = new P\Promise(); $promise = P\coroutine(function () use($a, $b) { // Execute the pool of commands concurrently, and process errors. (yield $a); (yield $b); })->otherwise(function (\Exception $e) { // Throw errors from the operations as a specific Multipart error. throw new \OutOfBoundsException('a', 0, $e); }); $a->resolve('a'); $b->reject('b'); $reason = P\inspect($promise)['reason']; $this->assertInstanceOf('OutOfBoundsException', $reason); $this->assertInstanceOf('GuzzleHttp\\Promise\\RejectionException', $reason->getPrevious()); }
/** * @param string $method * @param mixed $params * * @return PromiseInterface */ public function callAsync($method, $params) { $requestId = uniqid(); $options = $this->prepareCall($method, $params, $requestId); $response = $this->getClient()->postAsync('', $options); $promise = new Promise(function ($unwrap) use($response) { return $response->wait($unwrap); }, function () use($response) { return $response->cancel(); }); $response->then(function (ResponseInterface $res) use($promise) { try { $promise->resolve($this->handleResponse($res->getBody())); return $res; } catch (\Exception $e) { $promise->reject($e); throw $e; } }, function (\Exception $reqException) use($promise, $requestId) { $this->logResponse($requestId, strval($reqException)); $promise->reject($reqException); return $reqException; }); $promise->then(function ($res) use($requestId) { $this->logResponse($requestId, json_encode($res)); return $res; }, function ($res) use($requestId) { $this->logResponse($requestId, strval($res)); return $res; }); return $promise; }
/** * @param Promise $req * * @return Promise Promise with attached transformers */ private function attachTransforms(Promise $req) { $transformers = $this->getTransformers(); return $req->then(function (Response $res) use($transformers) { $data = \json_decode((string) $res->getBody()); if (is_object($data)) { foreach ($transformers as $transformer) { $data = \call_user_func($transformer, $data); } } elseif (is_array($data)) { foreach ($data as $key => $val) { foreach ($transformers as $transformer) { $data[$key] = \call_user_func($transformer, $val); } } } return $data; }, function (RequestException $e) { }); }
/** * @param string $targetUrl target url to import resource into * @param string $file path to file being loaded * @param OutputInterface $output output of the command * @param Document $doc document to load * @param string $host host to import into * @param string $rewriteHost string to replace with value from $host during loading * @param string $rewriteTo string to replace value from $rewriteHost with during loading * @param boolean $sync send requests syncronously * * @return Promise\Promise|null */ protected function importResource($targetUrl, $file, OutputInterface $output, Document $doc, $host, $rewriteHost, $rewriteTo, $sync = false) { $content = str_replace($rewriteHost, $rewriteTo, $doc->getContent()); $successFunc = function (ResponseInterface $response) use($output) { $output->writeln('<comment>Wrote ' . $response->getHeader('Link')[0] . '</comment>'); }; $errFunc = function (RequestException $e) use($output, $file) { $output->writeln('<error>' . str_pad(sprintf('Failed to write <%s> from \'%s\' with message \'%s\'', $e->getRequest()->getUri(), $file, $e->getMessage()), 140, ' ') . '</error>'); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $this->dumper->dump($this->cloner->cloneVar($this->parser->parse($e->getResponse()->getBody(), false, false, true)), function ($line, $depth) use($output) { if ($depth > 0) { $output->writeln('<error>' . str_pad(str_repeat(' ', $depth) . $line, 140, ' ') . '</error>'); } }); } }; if ($sync === false) { $promise = $this->client->requestAsync('PUT', $targetUrl, ['json' => $this->parseContent($content, $file)]); $promise->then($successFunc, $errFunc); } else { $promise = new Promise\Promise(); try { $promise->resolve($successFunc($this->client->request('PUT', $targetUrl, ['json' => $this->parseContent($content, $file)]))); } catch (BadResponseException $e) { $promise->resolve($errFunc($e)); } } return $promise; }
private function createSelfResolvingPromise($value) { $p = new Promise(function () use(&$p, $value) { $p->resolve($value); }); return $p; }
/** * {@inheritdoc} */ public function resolve() { $this->promise->wait(); }
echo "Username: {$username->wait()}\n"; // if you wanted to pass things all the way into a template you could do a trick like use GuzzleHttp\Promise\PromiseInterface; class StringWaiter { private $promise; public function __construct(PromiseInterface $promise) { $this->promise = $promise; } public function __toString() { try { return $this->promise->wait(); } catch (Exception $e) { return "Error: " . $e->getMessage() . "\n"; } } } $usernameApiPromise = $usernameClient->postAsync('generate-username'); $usernamePromise = new Promise(function () use($usernameApiPromise, &$usernamePromise) { $data = json_decode($usernameApiPromise->wait()->getBody()); $usernamePromise->resolve($data->username); }); $username = new StringWaiter($usernamePromise); echo "Username: {$username}\n"; // there is a bit of ugliness in making each call, but you shoudl be able to hide it behind some // helper functions. There is lots of power in the guzzle promises implmentation, but I find it // to be a little clunky in use. You will want to develop wrappers that give you the patterns // you want. // To finish up lets visit 5-async-timing.php and see how much time we are really saving
public function testUsesYieldedKeyInFulfilledCallback() { $r1 = new Promise(function () use(&$r1) { $r1->resolve(new Response()); }); $r2 = new Promise(function () use(&$r2) { $r2->resolve(new Response()); }); $r3 = new Promise(function () use(&$r3) { $r3->resolve(new Response()); }); $handler = new MockHandler([$r1, $r2, $r3]); $c = new Client(['handler' => $handler]); $keys = []; $requests = ['request_1' => new Request('GET', 'http://example.com'), 'request_2' => new Request('GET', 'http://example.com'), 'request_3' => new Request('GET', 'http://example.com')]; $p = new Pool($c, $requests, ['pool_size' => 2, 'fulfilled' => function ($res, $index) use(&$keys) { $keys[] = $index; }]); $p->promise()->wait(); $this->assertCount(3, $keys); $this->assertSame($keys, array_keys($requests)); }
/** * @param Promise $promise */ protected function wait(Promise $promise) { $promise->wait(); }
/** * Magic method which intercepts async calls, finds the sequential version, and wraps it in a * {@see Promise} object. In order for this to happen, the called methods need to be in the * following format: `createAsync`, where `create` is the sequential method being wrapped. * * @param $methodName The name of the method being invoked. * @param $args The arguments to be passed to the sequential method. * * @return Promise */ public function __call($methodName, $args) { if (substr($methodName, -5) === 'Async') { $realMethod = substr($methodName, 0, -5); if (!method_exists($this, $realMethod)) { throw new \InvalidArgumentException(sprintf('%s is not a defined method on %s', $realMethod, get_class($this))); } $promise = new Promise(function () use(&$promise, $realMethod, $args) { $value = call_user_func_array([$this, $realMethod], $args); $promise->resolve($value); }, function ($e) use(&$promise) { $promise->reject($e); }); return $promise; } }
/** * @param RequestInterface $request * @param array $options * @return Promise */ public function __invoke(RequestInterface $request, array $options) { $ready = false; $promise = new Promise(function () use(&$ready) { do { $this->loop->tick(); } while (!$ready); }); $this->requestFactory->create($request, $options, $this->httpClient, $this->loop)->then(function (ResponseInterface $response) use(&$ready, $promise) { $ready = true; $promise->resolve($response); $this->invokeQueue(); }, function ($error) use(&$ready, $promise) { $ready = true; $promise->reject($error); $this->invokeQueue(); }); return $promise; }