Promesas en la versión 3 de AWS SDK for PHP - AWS SDK for PHP

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Promesas en la versión 3 de AWS SDK for PHP

AWS SDK for PHP usa promesas para permitir los flujos de trabajo asíncronos y este carácter asíncrono permite enviar solicitudes HTTP simultáneas. La especificación de promesa que usa el SDK es Promises/A+.

¿Qué es una promesa?

Una promesa representa el resultado final de una operación asíncrona. La forma principal de interactuar con una promesa es mediante su método then. Este método registra las devoluciones de llamadas para recibir el valor final de la promesa o la razón por la que no se puede cumplir.

AWS SDK for PHP utiliza el paquete Composer guzzlehttp/promete en la implementación de sus promesas. Las promesas de Guzzle admiten flujos de trabajo con y sin bloqueo, y pueden usarse en cualquier bucle de eventos sin bloqueo.

nota

Las solicitudes HTTP se envían de forma simultánea en AWS SDK for PHP mediante un solo subproceso, en el que se usan llamadas sin bloqueo para transferir una o varias solicitudes HTTP, mientras que se reacciona a cambios de estado (por ejemplo, cuando se cumplen o incumplen promesas).

Promesas en el SDK

Las promesas se utilizan en todo el SDK. Por ejemplo, las promesas se utilizan en la mayoría de las abstracciones de alto nivel que proporciona el SDK: paginadores, esperadores, grupos de comandos, cargas multiparte, transferencias de directorio/bucket de S3, etc.

Todos los clientes que proporciona el SDK devuelven promesas cuando se invoca cualquiera de los métodos con el sufijo Async. Por ejemplo, el siguiente código muestra cómo crear una promesa para obtener los resultados de una operación DescribeTable de Amazon DynamoDB.

$client = new Aws\DynamoDb\DynamoDbClient([ 'region' => 'us-west-2', 'version' => 'latest', ]); // This will create a promise that will eventually contain a result $promise = $client->describeTableAsync(['TableName' => 'mytable']);

Observe que puede usar describeTable o describeTableAsync. Estos son los métodos __call mágicos para un cliente basados en el modelo de API y el número version asociados al cliente. Al invocar métodos como describeTable sin el sufijo Async, el cliente se bloquea mientras se envía una solicitud HTTP y se obtiene un objeto Aws\ResultInterface o una excepción Aws\Exception\AwsException. Al añadir al nombre de la operación el sufijo Async (es decir, al invocar describeTableAsync) el cliente creará una promesa que finalmente se cumplirá con un objeto Aws\ResultInterface o se rechazara con una excepción Aws\Exception\AwsException.

importante

Cuando se devuelve la promesa, puede que el resultado ya haya llegado (por ejemplo, cuando se utiliza un controlador simulado) o puede que la solicitud HTTP no se han iniciado.

Puede registrar una devolución de llamada con la promesa usando el método then. Este método acepta dos devoluciones de llamada: $onFulfilled y $onRejected, ambas opcionales. La devolución de llamada $onFulfilled se invoca si la promesa se cumple, y la devolución de llamada $onRejected se invoca si la promesa se rechaza (lo que significa que ha fallado).

$promise->then( function ($value) { echo "The promise was fulfilled with {$value}"; }, function ($reason) { echo "The promise was rejected with {$reason}"; } );

Ejecutar comandos simultáneamente

Es posible agrupar múltiples promesas para que se ejecuten de forma simultánea. Esto puede lograrse integrando el SDK con un bucle de eventos sin bloqueo, o creando múltiples promesas y esperando a que se completen de forma simultánea.

use GuzzleHttp\Promise\Utils; $sdk = new Aws\Sdk([ 'version' => 'latest', 'region' => 'us-east-1' ]); $s3 = $sdk->createS3(); $ddb = $sdk->createDynamoDb(); $promises = [ 'buckets' => $s3->listBucketsAsync(), 'tables' => $ddb->listTablesAsync(), ]; // Wait for both promises to complete. $results = Utils::unwrap($promises); // Notice that this method will maintain the input array keys. var_dump($results['buckets']->toArray()); var_dump($results['tables']->toArray());
nota

El objeto CommandPool proporciona un mecanismo más eficaz para ejecutar múltiples operaciones de la API de forma simultánea.

Encadenar promesas

Uno de los aspectos más interesantes de las promesas es pueden combinarse, lo que le permite crear canalizaciones de transformación. Las promesas se combinan encadenando devoluciones de llamada then con devoluciones de llamada then sucesivas. El valor que devuelve un método then es una promesa cumplida o rechazada en función del resultado de las devoluciones de llamadas indicadas.

$promise = $client->describeTableAsync(['TableName' => 'mytable']); $promise ->then( function ($value) { $value['AddedAttribute'] = 'foo'; return $value; }, function ($reason) use ($client) { // The call failed. You can recover from the error here and // return a value that will be provided to the next successful // then() callback. Let's retry the call. return $client->describeTableAsync(['TableName' => 'mytable']); } )->then( function ($value) { // This is only invoked when the previous then callback is // fulfilled. If the previous callback returned a promise, then // this callback is invoked only after that promise is // fulfilled. echo $value['AddedAttribute']; // outputs "foo" }, function ($reason) { // The previous callback was rejected (failed). } );
nota

El valor que devuelve una devolución de llamada de una promesa es el argumento $value que se suministra a promesas posteriores. Si desea especificar un valor para las promesas posteriores en la cadena, debe devolver un valor en la función de devolución de llamada.

Reenviar rechazos

Es posible registrar una devolución de llamada cuando se rechaza una promesa. Si cualquier devolución de llamada contiene una excepción, la promesa se rechaza con esa excepción, al igual que la siguiente promesa de la cadena. Si devuelve un valor correcto desde una devolución de llamada $onRejected la siguiente promesa de la cadena se cumplirá con el valor de retorno de la devolución de llamada $onRejected.

Esperar a las promesas

Para forzar que se complete una promesa de forma síncrona, puede usar su método wait.

$promise = $client->listTablesAsync(); $result = $promise->wait();

Si se encuentra una excepción al llamar a la función wait de una promesa, la promesa se rechaza con esa excepción y se genera una excepción.

use Aws\Exception\AwsException; $promise = $client->listTablesAsync(); try { $result = $promise->wait(); } catch (AwsException $e) { // Handle the error }

Cuando se invoca wait para una promesa que se ya se ha cumplido, no se activa la espera. Simplemente se devuelve el valor generado previamente.

$promise = $client->listTablesAsync(); $result = $promise->wait(); assert($result ### $promise->wait());

Cuando se invoca wait para una promesa que se ha rechazado, se genera una excepción. Si el motivo de rechazo es una instancia de \Exception, se genera el motivo como excepción. De lo contrario, se genera GuzzleHttp\Promise\RejectionException y el motivo puede obtenerse llamando al método getReason de la excepción.

nota

Las llamadas a operaciones API de AWS SDK for PHP se rechazan con subclases de la clase Aws\Exception\AwsException. Sin embargo, es posible que la razón indicada al método then sea diferente, ya que el uso del middleware personalizado modifica el motivo del rechazo.

Cancelar promesas

Las promesas se puede cancelar utilizando su método cancel(). Si una promesa ya se ha resuelto, llamar a cancel() no tendrá ningún efecto. La cancelación de una promesa cancela la promesa y cualesquiera otras que estén esperando su resultado. Las promesas canceladas se rechazan con GuzzleHttp\Promise\RejectionException.

Combinar promesas

Puede combinar promesas en grupos con los que crear flujos de trabajo más complejos. El paquete guzzlehttp/promise contiene distintas funciones que puede utilizar para combinar promesas.

Encontrará la documentación de la API con todas las funciones para combinar promesas en namespace-GuzzleHttp.Promise.

each y each_limit

Puede usar CommandPool cuando tenga una cola de tareas con comandos de Aws\CommandInterface que deban ejecutarse a la vez y el tamaño del grupo sea fijo (los comandos pueden estar en memoria o provenir de un iterador progresivo). CommandPool garantiza que un número fijo de comandos se envían de forma simultánea hasta que se agote el iterador.

CommandPool solo funciona con comandos que ejecuta un mismo cliente. Puede usar la función GuzzleHttp\Promise\each_limit para enviar comandos de diferentes clientes de forma simultánea con un número fijo de comandos.

use GuzzleHttp\Promise; $sdk = new Aws\Sdk([ 'version' => 'latest', 'region' => 'us-west-2' ]); $s3 = $sdk->createS3(); $ddb = $sdk->createDynamoDb(); // Create a generator that yields promises $promiseGenerator = function () use ($s3, $ddb) { yield $s3->listBucketsAsync(); yield $ddb->listTablesAsync(); // yield other promises as needed... }; // Execute the tasks yielded by the generator concurrently while limiting the // maximum number of concurrent promises to 5 $promise = Promise\each_limit($promiseGenerator(), 5); // Waiting on an EachPromise will wait on the entire task queue to complete $promise->wait();

Corutinas de promesas

Una de las características con más posibilidades de la biblioteca de promesas Guzzle es que permite utilizar corutinas de promesas que permiten escribir flujos de trabajo asíncronos de forma similar a los flujos de trabajo síncronos tradicionales. De hecho, AWS SDK for PHP utiliza promesas corutinas en la mayoría de las abstracciones de alto nivel.

Supongamos que desea crear varios buckets y cargar un archivo en un bucket cuando esté disponible, y que quiere ejecutar todas estas operaciones de forma simultánea para que se hagan lo más rápido posible. Es fácil conseguirlo combinando varias promesas corutinas mediante la función all().

use GuzzleHttp\Promise; $uploadFn = function ($bucket) use ($s3Client) { return Promise\coroutine(function () use ($bucket, $s3Client) { // You can capture the result by yielding inside of parens $result = (yield $s3Client->createBucket(['Bucket' => $bucket])); // Wait on the bucket to be available $waiter = $s3Client->getWaiter('BucketExists', ['Bucket' => $bucket]); // Wait until the bucket exists yield $waiter->promise(); // Upload a file to the bucket yield $s3Client->putObjectAsync([ 'Bucket' => $bucket, 'Key' => '_placeholder', 'Body' => 'Hi!' ]); }); }; // Create the following buckets $buckets = ['foo', 'baz', 'bar']; $promises = []; // Build an array of promises foreach ($buckets as $bucket) { $promises[] = $uploadFn($bucket); } // Aggregate the promises into a single "all" promise $aggregate = Promise\all($promises); // You can then() off of this promise or synchronously wait $aggregate->wait();