本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
AWS SDK for PHP版本 3 中的承諾
AWS SDK for PHP使用 promise 達成非同步工作流程,而這種非同步性讓您能夠並行傳送 HTTP 請求。由開發套件使用的 promise 規格是 Promises/A+
什麼是承諾?
Promise 代表非同步操作的最終結果。與 promise 主要互動的方式,是透過其 then
方法。此方法註冊回呼以接收 promise 的最終值或 promise 無法履行的理由。
AWS SDK for PHP倚賴 guzzlehttp/promises
注意
HTTP 請求在AWS SDK for PHP中使用單一執行緒同時發送,其中非封鎖呼叫用於傳輸一個以上的 HTTP 請求,同時回應狀態更改 (例如,履行或拒絕 promise)。
開發套件中的 Promise
Promise 用於整個軟體開發套件。例如,promise 用於由開發套件所提供的大多數高階抽象概念:分頁程式、等待程式、命令集區、分段上傳、S3 目錄/儲存貯體傳輸等。
當您呼叫用任何 Async
非同步尾碼方法時,開發套件提供的所有用戶端都會傳回 promise。例如,下列程式碼示範如何建立承諾以取得 Amazon DynamoDB DescribeTable
作業的結果。
$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
。這些方法是由 API 模型和與用戶端關聯的 __call
編號支援的用戶端神奇 version
方法。透過呼叫不帶 describeTable
尾碼的 Async
方法,用戶端將在傳送 HTTP 請求時封鎖,並返回 Aws\ResultInterface
物件或擲出 Aws\Exception\AwsException
。透過用 Async
(即 describeTableAsync
) 添加操作名稱尾碼,用戶端將建立一個最終由 Aws\ResultInterface
物件履行或被 Aws\Exception\AwsException
拒絕的 promise。
重要
當 promise 傳回時,結果可能已經到達 (例如,當使用模擬處理常式時),或者 HTTP 請求可能沒有啟動。
您可以使用 then
方法以 promise 註冊回呼。該方法接受兩個回呼,$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}";
}
);
同時執行命令
多個 promise 可以組合在一起,以便同時執行。這可以透過將軟體開發套件與非封鎖事件迴路整合,或透過建立多個 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供更強大的機制,可同時執行多個 API 作業。
鏈接承諾
Promise 的好處之一是它們可以組合,可讓您建立轉換管道。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 回呼的傳回值是提供給下游 promise 的 $value
引數。如果您要為下游 provide 鏈提供值,則必須在回呼函數中傳回一個值。
拒絕轉發
promise 被拒絕時,您可以註冊一個回呼以呼叫。如果在任何回呼中擲出例外,promise 將被例外拒絕,且鏈中的下一個 promise 將被拒絕並產生例外。如果您從 $onRejected
回呼成功傳回一個值,則 promise 鏈中的下一個 promise 將使用來自 $onRejected
回呼傳回的值以履行。
等待承諾
您可以透過使用 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
不會觸發等待函數。它只會傳回之前傳送的值。
$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 的 cancel()
方法取消 promise。如果 promise 已經解決,呼叫 cancel()
將不會生效。取消一個 promise 會取消該 promise,和任何等待該 promise 交付的 promise。一個遭到取消的 promise 會被 GuzzleHttp\Promise\RejectionException
拒絕。
結合承諾
您可以將 promise 結合到彙整 promise 中以建構更複雜的工作流程。guzzlehttp/promise
套件包含各種可用於結合 promise 的函數。
您可以在命名空間-.Promise 中找到所有承諾集合函數的 API 文檔GuzzleHttp。
each 和 each_limit
CommandPool當你有一個Aws\CommandInterface
命令的任務隊列時,使用固定池大小同時執行(這些命令可以在內存中或由延遲迭代器產生)。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();
承諾協同程序
Guzzle promise 程式庫一個更強大的功能,是它允許您使用 promise coroutine,讓編寫非同步工作流看起來更像是編寫傳統的同步工作流。實際上,AWS SDK for PHP在大多數高層級抽象中使用 coroutine promise。
假設在儲存貯體可用時,您希望建立多個儲存貯體並上傳檔案到儲存貯體中,且想同時完成這些事,以便能夠盡快發生。您可以透過使用 all()
promise 函數將多個 coroutine 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 = ['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();