

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用变量在状态之间传递数据
<a name="workflow-variables"></a>

**使用变量和 JSONata 管理状态**  
Step Functions 最近添加了变量和 JSONata 来管理状态和转换数据。  
要了解更多信息，请阅读博客文章[使用 Amazon Step Functions 中的变量和 JSONata 简化开发人员体验](https://www.amazonaws.cn/blogs/compute/simplifying-developer-experience-with-variables-and-jsonata-in-aws-step-functions/) 

 使用变量和状态输出，您可以在工作流的各个步骤之间传递数据。

 使用工作流变量，您可以在一个步骤中存储数据，并在将来的步骤中检索该数据。例如，您可以存储包含日后可能需要的数据的 API 响应。相反，状态输出只能用作紧接着下一步的输入。

## 变量的概念性概述
<a name="conceptual-overview-of-variables"></a>

 使用工作流变量，您可以存储数据以备日后参考。例如，步骤 1 可能会存储 API 请求的结果，以便之后在步骤 5 中重复使用该请求的一部分。

 在以下场景中，状态机从 API 获取数据一次。在步骤 1 中，工作流将返回的 API 数据（每个状态最多 256 KiB）存储在变量“x”中，以便在后续步骤中使用。

 如果不使用变量，则需要通过输出将该数据从步骤 1 依次传递到步骤 2、步骤 3、步骤 4，然后在步骤 5 中使用。如果这些中间步骤不需要该数据，怎么办？ 通过输出和输入在状态之间传递数据将不必要。

 借助变量，您可以存储数据并在将来的任何步骤中使用这些数据。您还可以在不中断数据流的情况下修改、重新排列或添加步骤。考虑到变量的灵活性，您可能只需使用 **Output** 来返回 Parallel 和 Map 子工作流中的数据，并在状态机执行结束时返回。

 ![\[Diagram showing step 1 assigning a value to $x, used in step 5.\]](http://docs.amazonaws.cn/step-functions/latest/dg/images/vars-diag-opt1.png)

 **支持变量的状态**

 以下状态类型支持 `Assign` 以声明变量并为其赋值：*Pass、Task、Map、Parallel、Choice、Wait。*

 要设置变量，请提供一个包含变量名称和值的 JSON 对象：

```
"Assign": {
  "productName": "product1",
  "count" : 42,
  "available" : true
}
```

 要引用变量，请在名称前面加上美元符号（`$`），例如 `$productName`。

## 保留变量：\$1states
<a name="reserved-variable-states"></a>

 Step Functions 定义了一个名为 **`$states`** 的保留变量。在 JSONata 状态下，将以下结构分配给 `$states` 以在 JSONata 表达式中使用：

```
# Reserved $states variable in JSONata states
$states = {
  "input":       // Original input to the state
  "result":      // API or sub-workflow's result (if successful)
  "errorOutput": // Error Output (only available in a Catch)
  "context":     // Context object
}
```

 进入状态时，Step Functions 会将状态输入分配给 **`$states.input`**。`$states.input` 的值可用于所有接受 JSONata 表达式的字段。`$states.input` 始终是指原始状态输入。

 对于 `Task`、`Parallel` 和 `Map` 状态：
+ **`$states.result`** 指 API 或子工作流成功时的原始结果。
+ **`$states.errorOutput`** 指 API 或子工作流失败时的错误输出。

  `$states.errorOutput` 可用于 `Catch` 字段的 `Assign` 或 `Output`。

如果在无法访问 `$states.result` 或 `$states.errorOutput` 的字段和状态中尝试对其进行访问，则在创建、更新或验证状态机时将捕获该尝试。

`$states.context` 对象为您的工作流提供有关其具体执行的信息，例如 `StartTime`、任务令牌和初始工作流输入。要了解更多信息，请参阅[在 Step Functions 中从上下文对象访问执行数据](input-output-contextobject.md)。

## 变量名称语法
<a name="variable-name-syntax"></a>

 变量名称遵循 [Unicode® 标准附录 \$131](https://unicode.org/reports/tr31/) 中所述的 Unicode 标识符规则。变量名称的第一个字符必须是 Unicode ID\$1Start 字符，第二个及后续字符必须是 Unicode ID\$1Continue 字符。变量名称的最大长度为 80 个字符。

 变量名称约定类似于 JavaScript 和其他编程语言的规则。

## 变量作用域
<a name="variable-scope"></a>

 Step Functions 工作流程通过使用 *workflow-local* 作用域来避免变量出现竞争条件。

workflow-local 作用域包括状态机**状态**字段内的所有状态，但不包括 Parallel 或 Map 状态内的状态。Parallel 或 Map 状态内的状态可以引用外部作用域变量，但它们创建和维护自己的独立 workflow-local 变量和值。

`Parallel` 分支和 `Map` 迭代可以访问**外部作用域**中的变量值，但它们无法访问其他并发分支或迭代中的变量值。处理错误时，`Catch` 中的 `Assign` 字段可以为外部作用域（即 Parallel/Map 状态存在的作用域）中的变量赋值。

 异常：**分布式 Map 状态**当前不能引用外部作用域中的变量。

 如果作用域中的任何状态为变量分配值，则该变量存在于作用域中。为避免常见错误，内部作用域中分配的变量不能与外部作用域中分配的变量同名。例如，如果顶级作用域为名为 `myVariable` 的变量赋值，则任何其他作用域（在 `Map`、`Parallel` 内）都不能同时为 `myVariable` 赋值。

 对变量的访问取决于当前作用域。Parallel 和 Map 状态都有各自的作用域，但它们能够访问外部作用域中的变量。

 当 Parallel 或 Map 状态完成时，它们的所有变量都将脱离作用域，不再可访问。使用 **Output 字段**将数据从 Parallel 分支和 Map 迭代中传出。

## 分配 ASL 中的字段
<a name="assign-field-in-asl"></a>

 ASL 中的 `Assign` 字段用于为一个或多个变量赋值。`Assign` 字段位于每个状态（`Succeed` 和 `Fail` 除外）的顶级、`Choice` 状态规则内部和 `Catch` 字段内部。例如：

```
# Example of Assign with JSONata
"Store inputs": {
    "Type": "Pass",
    "Next": "Get Current Price",
    "Comment": "Store the input desired price into a variable: $desiredPrice",
    "Assign": {
       "desiredPrice": "{% $states.input.desired_price %}",
       "maximumWait": "{% $states.input.max_days %}"
    }
},
```

 `Assign` 字段采用一个 JSON 对象。每个顶级字段都命名一个要赋值的变量。在前面的示例中，变量名称为 `desiredPrice` 和 `maximumWait`。使用 JSONata 时，`{% ... %}` 表示可能包含变量或更复杂表达式的 JSONata 表达式。有关 JSONata 表达式的更多信息，请参阅 [JSONata.org 文档](https://docs.jsonata.org/overview.html)。

 使用 **JSONata** 作为查询语言时，下图显示了如何并行处理 **Assign** 和 **Output** 字段。注意其含义：*为变量赋值并不会影响状态输出*。

 ![\[Diagram showing a comparison of JSONPath and JSONata flow.\]](http://docs.amazonaws.cn/step-functions/latest/dg/images/vars-jsonata.png)

 以下 JSONata 示例从状态输入中检索 `order.product`。变量 `currentPrice` 设置为任务结果中的一个值。

```
# Example of Task with JSONata assignment from result
{
   "Type": "Task",
   ...
   "Assign": {
      "product": "{% $states.input.order.product %}",
      "currentPrice": "{% $states.result.Payload.current_price %}"
   },
   "Next": "the next state"
}
```

 注意：**不能**为变量的一部分赋值。例如，可以 `"Assign":{"x":42}`，但不能 `"Assign":{"x.y":42}` 或 `"Assign":{"x[2]":42}`。

## 分配字段中的求值顺序
<a name="evaluation-order-in-an-assign-field"></a>

Step Functions 状态中的所有变量引用都使用**状态输入**时的值。

前述事实对于了解 `Assign` 字段如何为一个或多个变量赋值非常重要。首先，计算新值，然后 Step Functions 将新值分配给变量。新的变量值将在**下一个**状态开始时生效。例如，考虑以下 `Assign` 字段：

```
# Starting values: $x=3, $a=6

"Assign": {
  "x": "{% $a %}",
  "nextX": "{% $x %}"
}

# Ending values: $x=6, $nextX=3
```

在前述示例中，变量 `x` 既被赋值，又被引用。

请记住，所有表达式都是***先求值***，再进行赋值。新分配的值将在**下一个**状态下生效。

我们来详细看一下这个例子。假设在之前的状态下，为 `$x` 分配的值为三（3），为 `$a` 分配的值为六（6）。以下步骤描述了此过程：

1. 使用所有变量的**当前**值计算所有表达式。

   表达式 `"{% $a %}"` 的计算结果将为 6，`"{% $x %}"` 的计算结果为 3。

1. 接下来，进行赋值：

   将为 `$x` 分配六（6）的值 

   将为 `$nextX` 分配三（3）的值

 注意：如果 `$x` 之前未分配过，则该示例将**失败**，因为 `$x` 将是*未定义*。

 总而言之，Step Functions 会先计算**所有**表达式，再进行赋值。变量在 `Assign` 字段中出现的顺序并**不**重要。

## 限制
<a name="limits"></a>

 对于标准和快速工作流，单个变量的最大大小为 256Kib。

 单个 `Assign` 字段中所有变量的最大组合大小也为 256Kib。例如，您可以将 X 和 Y 分配为 128KiB 的大小，但不能在同一 `Assign` 字段中将 X 和 Y 都分配为 256KiB 的大小。

 每次执行时，所有已存储变量的总大小不能超过 10MiB。

## 在 JSONPath 状态下使用变量
<a name="using-variables-in-jsonpath-states"></a>

 在使用 JSONPath 作为查询语言的状态下，变量也可用。

 您可以在任何接受 JSONPath 表达式（`$.` 或 `$$.` 语法）的字段中引用变量，但 `ResultPath` 字段除外，该字段在状态输入中指定用于注入状态结果的位置。不能在 `ResultPath` 中使用变量。

 在 JSONPath 中，`$` 符号指的是“当前”值，`$$` 表示状态上下文对象。JSONPath 表达式可以像在 `$.customer.name` 中那样以 `$.` 开头。您可以像在 `$$.Execution.Id` 中那样使用 `$$.` 访问上下文。

 要引用变量，也可以在变量名前使用 `$` 符号，例如 `$x` 或 `$order.numItems`。

 例如，在接受内置函数的 **JSONPath** 字段中，可以在参数中使用变量，例如 `States.Format('The order number is {}', $order.number)`。

 下图说明了 **JSONPath** 任务中的分配步骤是如何与 ResultSelector 同时发生：

 ![\[Logical diagram of a state that uses JSONPath query language.\]](http://docs.amazonaws.cn/step-functions/latest/dg/images/vars-jsonpath.png)

 **在 JSONPath 中分配变量**

 JSONPath 变量分配的行为与有效载荷模板类似。以 `.$` 结尾的字段表示该值是一个 JSONPath 表达式，Step Functions 在状态机执行期间将其计算为一个值（例如：`$.order..product` 和 `$.order.total`）。

```
# Example of Assign with JSONPath
{
  "Type": "Task",
  ...
  "Assign": {
    "products.$": "$.order..product",
    "orderTotal.$": "$.order.total"
  },
  "Next": "the next state"
}
```

 对于 JSONPath 状态，`Assign` 字段中 `$` 的值取决于状态类型。在 `Task,`、`Map`、`Parallel` 状态下，`$` 指的是 API/子工作流结果。在 `Choice` 和 `Wait` 状态下，`$` 指的是*有效输入*，即 `InputPath` 应用于状态输入之后的值。对于 `Pass`，`$` 指的是结果，即是由 `Result` 字段生成，还是由 `InputPath`/`Parameters` 字段生成。

 下面的 JSONPath 示例将 JSON 对象分配给 `details` 变量，将 JSONPath 表达式 `$.result.code` 的结果分配给变量 `resultCode`，将 JSONPath 表达式 `States.Format('Hello {}', $customer.name)` 的结果分配给 `message`。如果处于 `Task` 状态，则 `$.order.items` 和 `$.result.code` 中的 `$` 指的是 API 结果。为 `startTime` 变量分配来自上下文对象 `$$.Execution.StartTime` 的值。

```
"Assign": {
   "details": {
      "status": "SUCCESS",
      "lineItems.$": "$.order.items"
   },
   "resultCode.$": "$.result.code",
   "message.$": "States.Format('Hello {}', $customer.name)",
   "startTime.$": "$$.Execution.StartTime"
}
```