

 適用於 JavaScript 的 AWS SDK v2 已end-of-support。我們建議您遷移至 [適用於 JavaScript 的 AWS SDK v3](https://docs.aws.amazon.com//sdk-for-javascript/v3/developer-guide/)。如需如何遷移的其他詳細資訊和資訊，請參閱此[公告](https://aws.amazon.com/blogs//developer/announcing-end-of-support-for-aws-sdk-for-javascript-v2/)。

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 非同步呼叫服務
<a name="calling-services-asynchronously"></a>

透過軟體開發套件提出的所有請求皆為非同步執行。編寫瀏覽器指令碼時，請務必注意這點。Web 瀏覽器中執行的 JavaScript 通常只有一個執行緒。對 AWS 服務進行非同步呼叫後，瀏覽器指令碼會繼續執行，而且在過程中可以嘗試在傳回之前執行取決於該非同步結果的程式碼。

對 AWS 服務進行非同步呼叫包括管理這些呼叫，讓您的程式碼在資料可用之前不會嘗試使用資料。本節中的主題會說明管理非同步呼叫的重要性，並詳細解說可用來管理這些呼叫的不同技術。

**Topics**
+ [管理非同步呼叫](making-asynchronous-calls.md)
+ [使用非同步回呼函數](using-a-callback-function.md)
+ [使用請求物件事件接聽程式](using-a-response-event-handler.md)
+ [使用 async/await](using-async-await.md)
+ [使用 JavaScript Promise](using-promises.md)

# 管理非同步呼叫
<a name="making-asynchronous-calls"></a>

舉例來說，回流的客戶能經由電子商務網站的首頁進行登入。對登入的客戶而言，這項功能的部分優點是網站會在客戶登入後，根據其特定偏好設定來自訂本身版面。為實現此目標，必須滿足以下條件：

1. 客戶必須使用其登入憑證登入並進行驗證。

1. 系統可從客戶資料庫請求客戶的偏好設定。

1. 資料庫需提供客戶的偏好設定，以便系統在載入網頁前使用該設定自訂網站。

如果您是同步執行這些任務，則每個任務必須在下一個任務開始之前完成。除非資料庫傳回客戶偏好設定，否則網頁將無法完成載入。但是，當系統將資料庫查詢傳送至伺服器後，網路瓶頸、異常高的資料庫流量，或是行動裝置連線品質不佳，都可能造成客戶資料接收延遲，甚至失敗。

請以非同步方式呼叫資料庫，避免網站因上述情況而停止運作。開始執行資料庫呼叫後，您能夠傳送非同步請求，讓程式碼能繼續正常運作。如果您沒有適當管理非同步呼叫的回應，程式碼就有可能在資料尚不可用的情況下，嘗試使用資料庫原先應回傳的相關資訊。

![\[說明同步和非同步執行間的差異。\]](http://docs.aws.amazon.com/zh_tw/sdk-for-javascript/v2/developer-guide/images/async-vs-sync.png)


# 使用非同步回呼函數
<a name="using-a-callback-function"></a>

能建立 `AWS.Request` 物件的每個服務物件方法，都能接受將非同步回呼函數做為最後一個參數。這類回呼函數的簽章如下所示：

```
function(error, data) {
    // callback handling code
}
```

當系統傳回成功回應或錯誤資料時，這類回呼函數即會開始執行。如果方法呼叫成功，回應內容便可供 `data` 參數中的回呼函數使用；如果呼叫不成功，則 `error` 參數會提供失敗的詳細資訊。

回呼函數內部的程式碼通常會測試錯誤，並處理傳回的錯誤。若沒有傳回錯誤，該程式碼就會擷取來自 `data` 參數的回應資料。回呼函數的基本形式如下方範例所示。

```
function(error, data) {
    if (error) {
        // error handling code
        console.log(error);
    } else {
        // data handling code
        console.log(data);
    }
}
```

在上述範例中，錯誤或所傳回資料的詳細資訊都會記錄到主控台。在接下來的範例中，系統會將傳遞的回呼函數做為呼叫服務物件方法的一部分。

```
new AWS.EC2({apiVersion: '2014-10-01'}).describeInstances(function(error, data) {
  if (error) {
    console.log(error); // an error occurred
  } else {
    console.log(data); // request succeeded
  }
});
```

## 存取請求和回應物件
<a name="access-request-response"></a>

在回呼函數內部，JavaScript 關鍵字 `this` 是指大多數服務的基礎 `AWS.Response` 物件。在下方範例中，系統會在回呼函數內使用 `AWS.Response` 物件的 `httpResponse` 屬性來記錄原始回應資料和標頭，以協助偵錯。

```
new AWS.EC2({apiVersion: '2014-10-01'}).describeInstances(function(error, data) {
  if (error) {
    console.log(error); // an error occurred
    // Using this keyword to access AWS.Response object and properties
    console.log("Response data and headers: " + JSON.stringify(this.httpResponse));
  } else {
    console.log(data); // request succeeded
  }
});
```

除此之外，`AWS.Response` 物件具備 `Request` 屬性，該屬性包含由原始方法呼叫傳送的 `AWS.Request`，因此您也能存取所發出請求的詳細資訊。

# 使用請求物件事件接聽程式
<a name="using-a-response-event-handler"></a>

在呼叫服務物件方法時，如果您沒有建立非同步回呼函數並將其做為參數傳遞，該方法便會產生 `AWS.Request` 物件，而此物件需要使用 `send` 方法手動傳送。

若要處理回應，您必須建立 `AWS.Request` 物件的事件接聽程式，藉此註冊回呼函數以進行方法呼叫。下方範例會說明如何建立用來呼叫服務物件方法的 `AWS.Request` 物件，以及成功回傳時所需的事件接聽程式。

```
// create the AWS.Request object
var request = new AWS.EC2({apiVersion: '2014-10-01'}).describeInstances();

// register a callback event handler
request.on('success', function(response) {
  // log the successful data response
  console.log(response.data); 
});

// send the request
request.send();
```

呼叫 `AWS.Request` 物件上的 `send` 方法後，事件處理常式即會在服務物件收到 `AWS.Response` 物件時開始執行。

如需`AWS.Request`物件的詳細資訊，請參閱《 API 參考[https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html)》中的 。如需`AWS.Response`物件的詳細資訊，請參閱 API 參考[https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html)中的 [使用回應物件](the-response-object.md)或 。

## 變更多個回呼
<a name="response-chaining-callbacks"></a>

您能夠在任何請求物件上註冊多個回呼，也可為不同事件或相同事件註冊多個回呼。而且，您還能鏈結回呼，如下方範例所示。

```
request.
  on('success', function(response) {
    console.log("Success!");
  }).
  on('error', function(response) {
    console.log("Error!");
  }).
  on('complete', function() {
    console.log("Always!");
  }).
  send();
```

## 請求物件完成事件
<a name="request-object-completion-events"></a>

根據每個服務操作方法的回應，`AWS.Request` 物件會引發下述完成事件：
+ `success`
+ `error`
+ `complete`

您能夠註冊回呼函數以回應任一事件。如需所有請求物件事件的完整清單，請參閱 API 參考[https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html)中的 。

### success 事件
<a name="request-success-event"></a>

收到來自服務物件的成功回應時，系統就會引發 `success` 事件。以下是針對這個事件註冊回呼函數的方式。

```
request.on('success', function(response) { 
  // event handler code
});
```

該回應會提供 `data` 屬性，其中包含來自服務的序列化回應資料。例如，以下呼叫 Amazon S3 服務物件的 `listBuckets`方法

```
s3.listBuckets.on('success', function(response) {
  console.log(response.data);
}).send();
```

系統即會傳回回應，然後將下列 `data` 屬性內容列印至主控台。

```
{ Owner: { ID: '...', DisplayName: '...' },
  Buckets: 
   [ { Name: 'someBucketName', CreationDate: someCreationDate },
     { Name: 'otherBucketName', CreationDate: otherCreationDate } ],
  RequestId: '...' }
```

### error 事件
<a name="request-error-event"></a>

收到來自服務物件的錯誤回應時，系統就會引發 `error` 事件。以下是針對這個事件註冊回呼函數的方式。

```
request.on('error', function(error, response) { 
  // event handling code
});
```

一旦引發 `error` 事件，回應的 `data` 屬性值將會是 `null`，而 `error` 屬性則會內含錯誤資料。系統會將相關聯的 `error` 物件做為第一個參數，並傳遞至註冊的回呼函數。以下方程式碼為例：

```
s3.config.credentials.accessKeyId = 'invalid';
s3.listBuckets().on('error', function(error, response) {
  console.log(error);
}).send();
```

系統即會傳回錯誤，然後將下列錯誤資料列印至主控台。

```
{ code: 'Forbidden', message: null }
```

### complete 事件
<a name="request-complete-event"></a>

系統會在服務物件呼叫完成時引發 `complete` 事件，無論呼叫成功或錯誤。以下是針對這個事件註冊回呼函數的方式。

```
request.on('complete', function(response) { 
  // event handler code
});
```

不管成功與否，請使用 `complete` 事件回呼來處理任何需要執行的請求清除作業。如果您要在 `complete` 事件的回呼內部使用回應資料，請先檢查 `response.data` 或 `response.error` 屬性，再嘗試存取其中一個屬性，如下方範例所示。

```
request.on('complete', function(response) {
  if (response.error) {
    // an error occurred, handle it
  } else {
    // we can use response.data here
  }
}).send();
```

## 請求物件 HTTP 事件
<a name="request-object-http-events"></a>

根據每個服務操作方法的回應，`AWS.Request` 物件會引發下述 HTTP 事件：
+ `httpHeaders`
+ `httpData`
+ `httpUploadProgress`
+ `httpDownloadProgress`
+ `httpError`
+ `httpDone`

您能夠註冊回呼函數以回應任一事件。如需所有請求物件事件的完整清單，請參閱《 API 參考[https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html)》中的 。

### httpHeaders 事件
<a name="request-httpheaders-event"></a>

當遠端伺服器傳送標頭時，系統就會引發 `httpHeaders` 事件。以下是針對這個事件註冊回呼函數的方式。

```
request.on('httpHeaders', function(statusCode, headers, response) {
  // event handling code
});
```

回呼函數的 `statusCode` 參數是 HTTP 狀態碼；`headers` 參數則包含回應標頭。

### httpData 事件
<a name="request-httpdata-event"></a>

系統會引發 `httpData` 事件，以便從服務串流回應資料封包。以下是針對這個事件註冊回呼函數的方式。

```
request.on('httpData', function(chunk, response) {
  // event handling code
});
```

將整個回應載入至無法實際使用的記憶體時，通常會使用這個事件來接收大型回應區塊。這個事件具有額外的 `chunk` 參數，其中包含一部分的伺服器實際資料。

若您針對 `httpData` 事件註冊回呼，回應的 `data` 屬性便會涵蓋請求的整個序列化輸出。如果您沒有內建處理常式所需的額外剖析和記憶體額外負荷，必須移除預設的 `httpData` 接聽程式。

### httpUploadProgress 和 httpDownloadProgress 事件
<a name="request-httpupload-download-progress-event"></a>

當 HTTP 請求上傳更多資料時，系統就會引發 `httpUploadProgress` 事件。同樣地，系統會在 HTTP 請求下載更多資料時引發 `httpDownloadProgress` 事件。以下是針對這些事件註冊回呼函數的方式。

```
request.on('httpUploadProgress', function(progress, response) {
  // event handling code
})
.on('httpDownloadProgress', function(progress, response) {
  // event handling code
});
```

回呼函數的 `progress` 參數中，包含具備載入請求和總位元組數的物件。

### httpError 事件
<a name="request-httperror-event"></a>

當 HTTP 請求失敗時，系統就會引發 `httpError` 事件。以下是針對這個事件註冊回呼函數的方式。

```
request.on('httpError', function(error, response) {
  // event handling code
});
```

回呼函數的 `error` 參數內含擲回的錯誤。

### httpDone 事件
<a name="request-httpdone-event"></a>

當伺服器完成資料傳送作業時，系統就會引發 `httpDone` 事件。以下是針對這個事件註冊回呼函數的方式。

```
request.on('httpDone', function(response) {
  // event handling code
});
```

# 使用 async/await
<a name="using-async-await"></a>

您可以在對 的呼叫中使用 `async/await` 模式 適用於 JavaScript 的 AWS SDK。接受回呼的大多數函數都不會傳回承諾。由於您只使用傳回承諾的`await`函數，若要使用`async/await`模式，您需要將`.promise()`方法鏈結至通話結尾，並移除回呼。

下列範例使用 async/await 列出 中的所有 Amazon DynamoDB 資料表`us-west-2`。

```
var AWS = require("aws-sdk");
//Create an Amazon DynamoDB client service object.
dbClient = new AWS.DynamoDB({ region: "us-west-2" });
// Call DynamoDB to list existing tables
const run = async () => {
  try {
    const results = await dbClient.listTables({}).promise();
    console.log(results.TableNames.join("\n"));
  } catch (err) {
    console.error(err);
  }
};
run();
```

**注意**  
 並非所有瀏覽器都支援非同步/等待。如需具有[非同步](https://caniuse.com/#feat=async-functions)/等待支援的瀏覽器清單，請參閱非同步函數。

# 使用 JavaScript Promise
<a name="using-promises"></a>

`AWS.Request.promise` 方法提供了一種能呼叫服務操作和管理非同步流程的方法，而非使用回呼。在 Node.js 與瀏覽器指令碼中，只要在沒有回呼函數的情況下呼叫服務操作，系統就會傳回 `AWS.Request` 物件。這時候，您能夠呼叫請求的 `send` 方法以進行服務呼叫。

然而，`AWS.Request.promise` 會立即開始呼叫服務，並傳回以回應 `data` 屬性履行的 promise，或是以回應 `error` 屬性拒絕的 promise。

```
var request = new AWS.EC2({apiVersion: '2014-10-01'}).describeInstances();

// create the promise object
var promise = request.promise();

// handle promise's fulfilled/rejected states
promise.then(
  function(data) {
    /* process the data */
  },
  function(error) {
    /* handle the error */
  }
);
```

接下來的範例會傳回以 `data` 物件履行，或是以 `error` 物件拒絕的 promise。使用 promise 時，單一回呼並不負責偵測錯誤。相反的，系統將根據請求成功或失敗來呼叫正確的回呼。

```
var s3 = new AWS.S3({apiVersion: '2006-03-01', region: 'us-west-2'});
var params = {
  Bucket: 'bucket',
  Key: 'example2.txt',
  Body: 'Uploaded text using the promise-based method!'
};
var putObjectPromise = s3.putObject(params).promise();
putObjectPromise.then(function(data) {
  console.log('Success');
}).catch(function(err) {
  console.log(err);
});
```

## 協調多個 promise
<a name="multiple-promises"></a>

在某些情況下，程式碼必須發出多個非同步呼叫，且唯有在這些呼叫全部成功回傳時，才需要採取動作。如果您要透過 promise 來管理個別非同步方法呼叫，則可建立採用 `all` 方法的額外 promise。您傳遞至方法的 promise 陣列都達成時，此方法即達成這個全域 promise。promise 傳遞至 `all` 方法的陣列值，會傳遞至回呼函數。

在下列範例中， AWS Lambda 函數必須對 Amazon DynamoDB 進行三次非同步呼叫，但只能在完成每次呼叫的承諾之後才能完成。

```
Promise.all([firstPromise, secondPromise, thirdPromise]).then(function(values) {
  
  console.log("Value 0 is " + values[0].toString);
  console.log("Value 1 is " + values[1].toString);
  console.log("Value 2 is " + values[2].toString);

  // return the result to the caller of the Lambda function
  callback(null, values);
});
```

## 瀏覽器和 Node.js 支援 Promise
<a name="browser-node-promise-support"></a>

對原生 JavaScript promise (ECMAScript 2015) 的支援將視程式碼執行的 JavaScript 引擎與版本而定。為協助您在需要執行程式碼的每個環境中，判斷其可支援的 JavaScript promise，請參閱 GitHub 上的 [ECMAScript 相容性資料表](https://compat-table.github.io/compat-table/es6/)。

## 使用其他 Promise 實作
<a name="using-other-promise-implementations"></a>

除了 ECMAScript 2015 中的原生 promise 實作外，您也能運用第三方的 promise 程式庫，包括：
+ [bluebird](http://bluebirdjs.com)
+ [RSVP](https://github.com/tildeio/rsvp.js/)
+ [Q](https://github.com/kriskowal/q)

如果您需要在不支援 ECMAScript 5 和 ECMAScript 2015 原生 promise 實作的環境中執行程式碼，則這些選用 promise 程式庫會很有幫助。

請呼叫全域組態物件的 `setPromisesDependency` 方法，並在軟體開發套件上設定 promise 相依性，即可使用第三方 promise 程式庫。在瀏覽器指令碼中，請務必先載入第三方 promise 程式庫，再載入軟體開發套件。在下方範例中，軟體開發套件已設定為使用 bluebird promise 程式庫中的實作。

```
AWS.config.setPromisesDependency(require('bluebird'));
```

若要恢復使用 JavaScript 引擎的原生 promise 實作，您可以再次呼叫 `setPromisesDependency` 並傳遞 `null`，而不傳送程式庫名稱。