

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS SDK for PHP バージョン 3 の promise
<a name="guide_promises"></a>

AWS SDK for PHP では **promise** を使用して非同期ワークフローの使用を許可します。この非同期性により、HTTP リクエストの同時送信が可能になります。SDK によって使用される promise の仕様は [Promises/A\$1](https://promisesaplus.com/) です。

## promise とは何ですか?
<a name="what-is-a-promise"></a>

*promise* は、非同期オペレーションの最終結果を表します。promise を操作する主な方法は、その `then` メソッドを介するものです。このメソッドは、promise の最終結果または実行できない理由を受け取るコールバックを登録します。

AWS SDK for PHP は、promise の実装を [guzzlehttp/promises](https://github.com/guzzle/promises) Composer パッケージに依存します。Guzzle promise はブロッキングとノンブロッキングワークフローをサポートし、ノンブロッキングループで使用できます。

**注記**  
AWS SDK for PHP で 1 つのスレッドを使用して、HTTP リクエストが同時に送信されます。ノンブロッキング呼び出しは、状態の変更 (promise の実行または拒否など) に対応する間に、1 つ以上の HTTP リクエストを転送するために使用されます。

## SDK での promise
<a name="promises-in-the-sdk"></a>

promise は、SDK 全体で使用されます。例えば、promise は SDK で提供されるほとんどの高レベル抽象化、[ページネーター](guide_paginators.md#async-paginators)、[ウェーター](guide_waiters.md#async-waiters)、[コマンドプール](guide_commands.md#command-pool)、[マルチパートアップロード](s3-multipart-upload.md)、[S3 ディレクトリ/バケット転送](s3-transfer.md)などで使用されます。

SDK が提供するすべてのクライアントは、`Async` が付いたメソッドのいずれかを呼び出した場合に promise を返します。例えば、次のコードでは、Amazon DynamoDB `DescribeTable` オペレーションの結果を取得するために promise を作成する方法を示しています。

```
$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']);
```

`describeTable` または `describeTableAsync` のどちらかを呼び出すことができることに注意してください。これらのメソッドは、クライアントのマジック `__call` メソッドで、クライアントに関連付けられている `version` 番号と API モデルで動いています。`describeTable` のような `Async` が付いていないメソッドを呼び出すことにより、HTTP リクエストを送信する間、クライアントはブロックし、`Aws\ResultInterface` オブジェクトを返すか `Aws\Exception\AwsException` をスローします。オペレーション名の後ろに `Async` を付けると (`describeTableAsync` など)、クライアントは promise を作成します。これは最終的に実行されると `Aws\ResultInterface` オブジェクト、または拒否されると `Aws\Exception\AwsException` になります。

**重要**  
promise が返されるとき、結果がすでに到着しています (たとえば、モックハンドラーを使用する場合)、または HTTP リクエストが開始されません。

`then` メソッドを使用して、promise とコールバックを登録できます。このメソッドは、2 つのコールバック、`$onFulfilled` と `$onRejected` (どちらも省略可能) を受け付けます。promise が実行される場合に、`$onFulfilled` コールバックが呼び出され、promise が拒否される (失敗した) 場合に、`$onRejected` コールバックが呼び出されます。

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

### コマンドの同時実行
<a name="executing-commands-concurrently"></a>

複数の promise は同時に実行されるようにまとめて構成できます。これを行うには、SDK とノンブロッキングイベントループを統合する、複数の promise を作成し、同時に完了するまで待機します。

```
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());
```

**注記**  
[CommandPool](guide_commands.md#command-pool) には複数の API オペレーションを同時に実行するためのさらに強力なメカニズムが用意されています。

## promise の連鎖
<a name="chaining-promises"></a>

promise のすばらしい特徴の 1 つは、組み合わせられること、つまり変換パイプラインを作成することができます。複数の promise は `then` コールバックと後続の `then` コールバックを連結することで構成されます。`then` メソッドの戻り値が promise です。指定したコールバックの結果に基づいて、実行されるか拒否されます。

```
$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).
        }
    );
```

**注記**  
promise コールバックの戻り値は `$value` 引数で、ダウンストリームの promise に渡されます。ダウンストリームの promise チェーンに値を指定する場合は、コールバック関数に値を返す必要があります。

### 拒否の転送
<a name="rejection-forwarding"></a>

promise が拒否されたときに呼び出すコールバックを登録することができます。任意のコールバックで例外がスローされた場合、promise は例外で拒否され、チェーンの次の promise も例外で拒否されます。`$onRejected` コールバックから正常な値を返す場合、promise チェーンの次の promise が `$onRejected` コールバックからの戻り値で実行されます。

## promise の待機
<a name="waiting-on-promises"></a>

promise の `wait` メソッドを使用すると、promise が同期的に完了するように強制できます。

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

promise の `wait` 関数を呼び出している間に例外が発生した場合は、例外により promise が拒否され、例外がスローされます。

```
use Aws\Exception\AwsException;

$promise = $client->listTablesAsync();

try {
    $result = $promise->wait();
} catch (AwsException $e) {
    // Handle the error
}
```

実行された promise での `wait` の呼び出しは wait 関数をトリガーしません。以前に配信された値だけを返します。

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

拒否された promise での `wait` の呼び出しでは例外がスローされます。拒否理由が `\Exception` のインスタンスである場合、その理由がスローされます。それ以外の場合は、`GuzzleHttp\Promise\RejectionException` がスローされ、例外の `getReason` メソッドを呼び出して理由を取得できます。

**注記**  
AWS SDK for PHP での API オペレーションの呼び出しは `Aws\Exception\AwsException` クラスのサブクラスで拒否されます。ただし、`then` メソッドが配信する理由が異なる場合もあります。拒否理由を変更するカスタムミドルウェアを追加できるためです。

## promise のキャンセル
<a name="canceling-promises"></a>

promise の `cancel()` メソッドを使用して、promise をキャンセルできます。promise がすでに解決された場合、`cancel()` を呼び出しても何も起きません。promise のキャンセルは、promise 自体とそこからの配信を待機している promise をキャンセルします。キャンセルされた promise は `GuzzleHttp\Promise\RejectionException` で拒否されます。

## promise の結合
<a name="combining-promises"></a>

promise を組み合わせて複合 promise を作成し、複雑なワークフローを構築できます。`guzzlehttp/promise` パッケージには、promise を結合するために使用できるさまざまな関数が含まれています。

promise コレクション関数のすべての API ドキュメントを見つけるには、[namespace-GuzzleHttp.Promise](https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-GuzzleHttp.Promise.html) を参照してください。

### each および each\$1limit
<a name="each-and-each-limit"></a>

`Aws\CommandInterface` コマンドのタスクキューがあるとき、[CommandPool](guide_commands.md#command-pool) を使用して、固定プールサイズ (コマンドをメモリに格納または遅延イテレーターで出力可能) で同時に実行します。`CommandPool` により、固定数のコマンドは、指定されたイテレーターを使い果たすまで同時に送信されます。

`CommandPool` は、同じクライアントで実行されたコマンドの場合にのみ機能します。固定プールサイズを使用して、異なるクライアントにコマンドを同時に送信するために、`GuzzleHttp\Promise\each_limit` 関数を使用できます。

```
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();
```

### promise コルーチン
<a name="promise-coroutines"></a>

Guzzle promise ライブラリの強力な機能の 1 つは、従来の同期のワークフローを記述するように、非同期ワークフローを記述できる promise コルーチンを使用できることです。実際、AWS SDK for PHP ではほとんどの高レベルの抽象化で、コルーチン promise を使用します。

複数のバケットを作成し、バケットが利用可能になったときに、バケットにファイルをアップロードする場合、できる限り迅速に完了するようにすべて同時に実行することを考えます。これを行うには、`all()` promise 関数を使用して複数のコルーチン promise を組み合わせます。

```
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 = ['amzn-s3-demo-bucket1', 'amzn-s3-demo-bucket2', 'amzn-s3-demo-bucket3'];
$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();
```