

适用于 JavaScript 的 AWS SDK v2 已终止支持。建议您迁移到 [适用于 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>

通过 SDK 发出的所有请求均为异步。在编写浏览器脚本时，务必记住这一点。在 Web 浏览器中运行的 JavaScript 通常只有一个执行线程。在对 AWS 服务进行异步调用之后，浏览器脚本继续运行，并可在该过程中结果返回之前，尝试执行依赖于该异步结果的代码。

对 AWS 服务进行异步调用包括管理这些调用，使得您的代码不会在数据可用之前尝试使用这些数据。本部分中的主题说明管理异步调用的需求，以及在管理它们时可以使用的具体不同技术。

**Topics**
+ [管理异步调用](making-asynchronous-calls.md)
+ [使用匿名回调函数](using-a-callback-function.md)
+ [使用请求对象事件侦听器](using-a-response-event-handler.md)
+ [使用异步/等待](using-async-await.md)
+ [使用 JavaScript Promise](using-promises.md)

# 管理异步调用
<a name="making-asynchronous-calls"></a>

例如，电子商务网站的主页会让返回的客户登录。客户登录可以获得的一部分好处在于，登录之后网站可以根据其特定首选项进行自定义。要做到这一点：

1. 客户必须登录并使用其登录凭证进行验证。

1. 从客户数据库中请求客户的首选项。

1. 数据库提供客户的首选项，这些首选项用于在页面加载之前自定义网站。

如果这些任务同步执行，则必须在每个任务完成之后才能执行下一个任务。在数据库返回客户首选项之前，网页无法完成加载。但是，在数据库查询发送到服务器之后，由于网络瓶颈、极高的数据库流量或者糟糕的移动设备连接，客户数据的接收可能会延迟甚至失败。

要避免网站在这些情况下停滞不前，可以异步调用数据库。数据库调用执行之后，发送您的异步请求，您的代码继续按预期方式执行。如果您未能正确地管理异步调用的响应，代码会在数据尚不可用时，尝试使用预期从数据库返回的信息。

![\[显示同步和异步执行之间的差别。\]](http://docs.aws.amazon.com/zh_cn/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` 对象。在以下示例中，`httpResponse` 对象的 `AWS.Response` 属性在回调函数中用于记录原始响应数据和标头，以帮助调试。

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

在调用了 `send` 上的 `AWS.Request` 方法之后，当服务对象收到 `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` 对象的更多信息，请参阅[使用响应对象](the-response-object.md)或 API 参考中的 [https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html)。

## 链接多个回调
<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
});
```

# 使用异步/等待
<a name="using-async-await"></a>

您可以在调用 适用于 JavaScript 的 AWS SDK 时使用 `async/await` 模式。大多数接受回调的函数都不会返回 promise。由于您只使用返回 promise 的 `await` 函数，因此要使用 `async/await` 模式，您需要将 `.promise()` 方法链接到调用的末尾并移除回调。

以下示例使用 async/await 来列出您在 `us-west-2` 中的所有 Amazon DynamoDB 表。

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

**注意**  
 并非所有浏览器都支持 async/await。有关支持异步/等待的浏览器列表，请参阅[异步函数](https://caniuse.com/#feat=async-functions)。

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

`AWS.Request.promise` 方法提供了一种方式来调用服务操作和管理异步流，而不使用回调。在 Node.js 和浏览器脚本中，未使用回调函数调用了服务操作时，将返回 `AWS.Request` 对象。您可以调用请求的 `send` 方法来发出服务调用。

但是，`AWS.Request.promise` 立即启动服务调用并返回 promise，其中通过响应 `data` 属性来执行，或者通过响应 `error` 属性来拒绝。

```
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 */
  }
);
```

接下来的示例返回 promise，其中通过 `data` 对象来执行，或者通过 `error` 对象来拒绝。使用 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 时才能完成。

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

## Promise 的浏览器和 Node.js 支持
<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 库会非常有用。

要使用第三方 promise 库，请通过在全局配置对象上调用 `setPromisesDependency` 方法，在开发工具包上设置 promise 依赖关系。在浏览器脚本中，请确保先加载第三方 promise 库，然后加载开发工具包。在以下示例中，开发工具包配置为使用 bluebird promise 库中的实施。

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

要返回使用 JavaScript 引擎的原生 promise 实施，请再次调用 `setPromisesDependency`，传递 `null` 而不是库名。