Skip to content

Commit 3d6b793

Browse files
authored
Merge pull request #97392 from anthonychu/20191128-tabify-durable-overview
Add language tabs to Durable Functions overview
2 parents 0f5f373 + 98c54b7 commit 3d6b793

File tree

1 file changed

+87
-48
lines changed

1 file changed

+87
-48
lines changed

articles/azure-functions/durable/durable-functions-overview.md

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ ms.reviewer: azfuncdf
1818
Durable Functions currently supports the following languages:
1919

2020
* **C#**: both [precompiled class libraries](../functions-dotnet-class-library.md) and [C# script](../functions-reference-csharp.md).
21-
* **F#**: precompiled class libraries and F# script. F# script is only supported for version 1.x of the Azure Functions runtime.
2221
* **JavaScript**: supported only for version 2.x of the Azure Functions runtime. Requires version 1.7.0 of the Durable Functions extension, or a later version.
22+
* **F#**: precompiled class libraries and F# script. F# script is only supported for version 1.x of the Azure Functions runtime.
2323

2424
Durable Functions has a goal of supporting all [Azure Functions languages](../supported-languages.md). See the [Durable Functions issues list](https://github.com/Azure/azure-functions-durable-extension/issues) for the latest status of work to support additional languages.
2525

@@ -34,17 +34,19 @@ The primary use case for Durable Functions is simplifying complex, stateful coor
3434
* [Async HTTP APIs](#async-http)
3535
* [Monitoring](#monitoring)
3636
* [Human interaction](#human)
37-
* [Aggregator](#aggregator)
37+
* [Aggregator (stateful entities)](#aggregator)
3838

3939
### <a name="chaining"></a>Pattern #1: Function chaining
4040

4141
In the function chaining pattern, a sequence of functions executes in a specific order. In this pattern, the output of one function is applied to the input of another function.
4242

4343
![A diagram of the function chaining pattern](./media/durable-functions-concepts/function-chaining.png)
4444

45-
You can use Durable Functions to implement the function chaining pattern concisely as shown in the following example:
45+
You can use Durable Functions to implement the function chaining pattern concisely as shown in the following example.
4646

47-
#### C#
47+
In this example, the values `F1`, `F2`, `F3`, and `F4` are the names of other functions in the function app. You can implement control flow by using normal imperative coding constructs. Code executes from the top down. The code can involve existing language control flow semantics, like conditionals and loops. You can include error handling logic in `try`/`catch`/`finally` blocks.
48+
49+
# [C#](#tab/csharp)
4850

4951
```csharp
5052
[FunctionName("Chaining")]
@@ -65,25 +67,31 @@ public static async Task<object> Run(
6567
}
6668
```
6769

68-
#### JavaScript (Functions 2.0 only)
70+
You can use the `context` parameter to invoke other functions by name, pass parameters, and return function output. Each time the code calls `await`, the Durable Functions framework checkpoints the progress of the current function instance. If the process or virtual machine recycles midway through the execution, the function instance resumes from the preceding `await` call. For more information, see the next section, Pattern #2: Fan out/fan in.
71+
72+
# [JavaScript](#tab/javascript)
6973

7074
```javascript
7175
const df = require("durable-functions");
7276

7377
module.exports = df.orchestrator(function*(context) {
74-
const x = yield context.df.callActivity("F1");
75-
const y = yield context.df.callActivity("F2", x);
76-
const z = yield context.df.callActivity("F3", y);
77-
return yield context.df.callActivity("F4", z);
78+
try {
79+
const x = yield context.df.callActivity("F1");
80+
const y = yield context.df.callActivity("F2", x);
81+
const z = yield context.df.callActivity("F3", y);
82+
return yield context.df.callActivity("F4", z);
83+
} catch (error) {
84+
// Error handling or compensation goes here.
85+
}
7886
});
7987
```
8088

81-
In this example, the values `F1`, `F2`, `F3`, and `F4` are the names of other functions in the function app. You can implement control flow by using normal imperative coding constructs. Code executes from the top down. The code can involve existing language control flow semantics, like conditionals and loops. You can include error handling logic in `try`/`catch`/`finally` blocks.
82-
83-
You can use the `context` parameter [IDurableOrchestrationContext] \(.NET\) and the `context.df` object (JavaScript) to invoke other functions by name, pass parameters, and return function output. Each time the code calls `await` (C#) or `yield` (JavaScript), the Durable Functions framework checkpoints the progress of the current function instance. If the process or VM recycles midway through the execution, the function instance resumes from the preceding `await` or `yield` call. For more information, see the next section, Pattern #2: Fan out/fan in.
89+
You can use the `context.df` object to invoke other functions by name, pass parameters, and return function output. Each time the code calls `yield`, the Durable Functions framework checkpoints the progress of the current function instance. If the process or virtual machine recycles midway through the execution, the function instance resumes from the preceding `yield` call. For more information, see the next section, Pattern #2: Fan out/fan in.
8490

8591
> [!NOTE]
86-
> The `context` object in JavaScript represents the entire [function context](../functions-reference-node.md#context-object), not only the [IDurableOrchestrationContext] parameter.
92+
> The `context` object in JavaScript represents the entire [function context](../functions-reference-node.md#context-object). Access the Durable Functions context using the `df` property on the main context.
93+
94+
---
8795

8896
### <a name="fan-in-out"></a>Pattern #2: Fan out/fan in
8997

@@ -95,7 +103,7 @@ With normal functions, you can fan out by having the function send multiple mess
95103

96104
The Durable Functions extension handles this pattern with relatively simple code:
97105

98-
#### C#
106+
# [C#](#tab/csharp)
99107

100108
```csharp
101109
[FunctionName("FanOutFanIn")]
@@ -120,7 +128,11 @@ public static async Task Run(
120128
}
121129
```
122130

123-
#### JavaScript (Functions 2.0 only)
131+
The fan-out work is distributed to multiple instances of the `F2` function. The work is tracked by using a dynamic list of tasks. `Task.WhenAll` is called to wait for all the called functions to finish. Then, the `F2` function outputs are aggregated from the dynamic task list and passed to the `F3` function.
132+
133+
The automatic checkpointing that happens at the `await` call on `Task.WhenAll` ensures that a potential midway crash or reboot doesn't require restarting an already completed task.
134+
135+
# [JavaScript](#tab/javascript)
124136

125137
```javascript
126138
const df = require("durable-functions");
@@ -142,9 +154,11 @@ module.exports = df.orchestrator(function*(context) {
142154
});
143155
```
144156

145-
The fan-out work is distributed to multiple instances of the `F2` function. The work is tracked by using a dynamic list of tasks. The .NET `Task.WhenAll` API or JavaScript `context.df.Task.all` API is called, to wait for all the called functions to finish. Then, the `F2` function outputs are aggregated from the dynamic task list and passed to the `F3` function.
157+
The fan-out work is distributed to multiple instances of the `F2` function. The work is tracked by using a dynamic list of tasks. `context.df.Task.all` API is called to wait for all the called functions to finish. Then, the `F2` function outputs are aggregated from the dynamic task list and passed to the `F3` function.
146158

147-
The automatic checkpointing that happens at the `await` or `yield` call on `Task.WhenAll` or `context.df.Task.all` ensures that a potential midway crash or reboot doesn't require restarting an already completed task.
159+
The automatic checkpointing that happens at the `yield` call on `context.df.Task.all` ensures that a potential midway crash or reboot doesn't require restarting an already completed task.
160+
161+
---
148162

149163
> [!NOTE]
150164
> In rare circumstances, it's possible that a crash could happen in the window after an activity function completes but before its completion is saved into the orchestration history. If this happens, the activity function would re-run from the beginning after the process recovers.
@@ -196,11 +210,11 @@ An example of the monitor pattern is to reverse the earlier async HTTP API scena
196210

197211
![A diagram of the monitor pattern](./media/durable-functions-concepts/monitor.png)
198212

199-
In a few lines of code, you can use Durable Functions to create multiple monitors that observe arbitrary endpoints. The monitors can end execution when a condition is met, or the `IDurableOrchestrationClient` can terminate the monitors. You can change a monitor's `wait` interval based on a specific condition (for example, exponential backoff.)
213+
In a few lines of code, you can use Durable Functions to create multiple monitors that observe arbitrary endpoints. The monitors can end execution when a condition is met, or another function can use the durable orchestration client to terminate the monitors. You can change a monitor's `wait` interval based on a specific condition (for example, exponential backoff.)
200214

201215
The following code implements a basic monitor:
202216

203-
#### C#
217+
# [C#](#tab/csharp)
204218

205219
```csharp
206220
[FunctionName("MonitorJobStatus")]
@@ -230,7 +244,7 @@ public static async Task Run(
230244
}
231245
```
232246

233-
#### JavaScript (Functions 2.0 only)
247+
# [JavaScript](#tab/javascript)
234248

235249
```javascript
236250
const df = require("durable-functions");
@@ -258,7 +272,9 @@ module.exports = df.orchestrator(function*(context) {
258272
});
259273
```
260274

261-
When a request is received, a new orchestration instance is created for that job ID. The instance polls a status until a condition is met and the loop is exited. A durable timer controls the polling interval. Then, more work can be performed, or the orchestration can end. When the `context.CurrentUtcDateTime` (.NET) or `context.df.currentUtcDateTime` (JavaScript) exceeds the `expiryTime` value, the monitor ends.
275+
---
276+
277+
When a request is received, a new orchestration instance is created for that job ID. The instance polls a status until a condition is met and the loop is exited. A durable timer controls the polling interval. Then, more work can be performed, or the orchestration can end. When `nextCheck` exceeds `expiryTime`, the monitor ends.
262278

263279
### <a name="human"></a>Pattern #5: Human interaction
264280

@@ -272,7 +288,7 @@ You can implement the pattern in this example by using an orchestrator function.
272288

273289
These examples create an approval process to demonstrate the human interaction pattern:
274290

275-
#### C#
291+
# [C#](#tab/csharp)
276292

277293
```csharp
278294
[FunctionName("ApprovalWorkflow")]
@@ -299,7 +315,9 @@ public static async Task Run(
299315
}
300316
```
301317

302-
#### JavaScript (Functions 2.0 only)
318+
To create the durable timer, call `context.CreateTimer`. The notification is received by `context.WaitForExternalEvent`. Then, `Task.WhenAny` is called to decide whether to escalate (timeout happens first) or process the approval (the approval is received before timeout).
319+
320+
# [JavaScript](#tab/javascript)
303321

304322
```javascript
305323
const df = require("durable-functions");
@@ -321,9 +339,19 @@ module.exports = df.orchestrator(function*(context) {
321339
});
322340
```
323341

324-
To create the durable timer, call `context.CreateTimer` (.NET) or `context.df.createTimer` (JavaScript). The notification is received by `context.WaitForExternalEvent` (.NET) or `context.df.waitForExternalEvent` (JavaScript). Then, `Task.WhenAny` (.NET) or `context.df.Task.any` (JavaScript) is called to decide whether to escalate (timeout happens first) or process the approval (the approval is received before timeout).
342+
To create the durable timer, call `context.df.createTimer`. The notification is received by `context.df.waitForExternalEvent`. Then, `context.df.Task.any` is called to decide whether to escalate (timeout happens first) or process the approval (the approval is received before timeout).
343+
344+
---
345+
346+
An external client can deliver the event notification to a waiting orchestrator function by using the [built-in HTTP APIs](durable-functions-http-api.md#raise-event):
347+
348+
```bash
349+
curl -d "true" http://localhost:7071/runtime/webhooks/durabletask/instances/{instanceId}/raiseEvent/ApprovalEvent -H "Content-Type: application/json"
350+
```
351+
352+
An event can also be raised using the durable orchestration client from another function:
325353

326-
An external client can deliver the event notification to a waiting orchestrator function by using either the [built-in HTTP APIs](durable-functions-http-api.md#raise-event) or by using the `RaiseEventAsync` (.NET) or `raiseEvent` (JavaScript) method from another function:
354+
# [C#](#tab/csharp)
327355

328356
```csharp
329357
[FunctionName("RaiseEventToOrchestration")]
@@ -336,6 +364,8 @@ public static async Task Run(
336364
}
337365
```
338366

367+
# [JavaScript](#tab/javascript)
368+
339369
```javascript
340370
const df = require("durable-functions");
341371

@@ -346,11 +376,9 @@ module.exports = async function (context) {
346376
};
347377
```
348378

349-
```bash
350-
curl -d "true" http://localhost:7071/runtime/webhooks/durabletask/instances/{instanceId}/raiseEvent/ApprovalEvent -H "Content-Type: application/json"
351-
```
379+
---
352380

353-
### <a name="aggregator"></a>Pattern #6: Aggregator
381+
### <a name="aggregator"></a>Pattern #6: Aggregator (stateful entities)
354382

355383
The sixth pattern is about aggregating event data over a period of time into a single, addressable *entity*. In this pattern, the data being aggregated may come from multiple sources, may be delivered in batches, or may be scattered over long-periods of time. The aggregator might need to take action on event data as it arrives, and external clients may need to query the aggregated data.
356384

@@ -360,6 +388,8 @@ The tricky thing about trying to implement this pattern with normal, stateless f
360388

361389
You can use [Durable entities](durable-functions-entities.md) to easily implement this pattern as a single function.
362390

391+
# [C#](#tab/csharp)
392+
363393
```csharp
364394
[FunctionName("Counter")]
365395
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
@@ -381,6 +411,28 @@ public static void Counter([EntityTrigger] IDurableEntityContext ctx)
381411
}
382412
```
383413

414+
Durable entities can also be modeled as classes in .NET. This model can be useful if the list of operations is fixed and becomes large. The following example is an equivalent implementation of the `Counter` entity using .NET classes and methods.
415+
416+
```csharp
417+
public class Counter
418+
{
419+
[JsonProperty("value")]
420+
public int CurrentValue { get; set; }
421+
422+
public void Add(int amount) => this.CurrentValue += amount;
423+
424+
public void Reset() => this.CurrentValue = 0;
425+
426+
public int Get() => this.CurrentValue;
427+
428+
[FunctionName(nameof(Counter))]
429+
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
430+
=> ctx.DispatchAsync<Counter>();
431+
}
432+
```
433+
434+
# [JavaScript](#tab/javascript)
435+
384436
```javascript
385437
const df = require("durable-functions");
386438

@@ -401,28 +453,12 @@ module.exports = df.entity(function(context) {
401453
});
402454
```
403455

404-
Durable entities can also be modeled as classes in .NET. This model can be useful if the list of operations is fixed and becomes large. The following example is an equivalent implementation of the `Counter` entity using .NET classes and methods.
405-
406-
```csharp
407-
public class Counter
408-
{
409-
[JsonProperty("value")]
410-
public int CurrentValue { get; set; }
411-
412-
public void Add(int amount) => this.CurrentValue += amount;
413-
414-
public void Reset() => this.CurrentValue = 0;
415-
416-
public int Get() => this.CurrentValue;
417-
418-
[FunctionName(nameof(Counter))]
419-
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
420-
=> ctx.DispatchAsync<Counter>();
421-
}
422-
```
456+
---
423457

424458
Clients can enqueue *operations* for (also known as "signaling") an entity function using the [entity client binding](durable-functions-bindings.md#entity-client).
425459

460+
# [C#](#tab/csharp)
461+
426462
```csharp
427463
[FunctionName("EventHubTriggerCSharp")]
428464
public static async Task Run(
@@ -441,6 +477,7 @@ public static async Task Run(
441477
> [!NOTE]
442478
> Dynamically generated proxies are also available in .NET for signaling entities in a type-safe way. And in addition to signaling, clients can also query for the state of an entity function using [type-safe methods](durable-functions-bindings.md#entity-client-usage) on the orchestration client binding.
443479
480+
# [JavaScript](#tab/javascript)
444481

445482
```javascript
446483
const df = require("durable-functions");
@@ -452,6 +489,8 @@ module.exports = async function (context) {
452489
};
453490
```
454491

492+
---
493+
455494
Entity functions are available in [Durable Functions 2.0](durable-functions-versions.md) and above.
456495

457496
## The technology

0 commit comments

Comments
 (0)