Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,6 @@ In spite of this, Durable Functions ensures reliable execution of orchestrations

The `durable-functions` shim lets you express a workflow in code as a [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) wrapped by a call to the `orchestrator` method. `orchestrator` treats `yield`-ed calls to your function `context`'s `df` object, like `context.df.callActivity`, as points where you want to schedule an asynchronous unit of work and wait for it to complete.

These calls return a `Task` or `TaskSet` object signifying the outstanding work. The `orchestrator` method appends the action(s) of the `Task` or `TaskSet` object to a list which it passes back to the Functions runtime, plus whether the function is completed, and any output or errors.
These calls return a `Task` object signifying the outstanding work. The `orchestrator` method appends the action(s) of the `Task` object to a list which it passes back to the Functions runtime, plus whether the function is completed, and any output or errors.

The Azure Functions extension schedules the desired actions. When the actions complete, the extension triggers the orchestrator function to replay up to the next incomplete asynchronous unit of work or its end, whichever comes first.
2 changes: 1 addition & 1 deletion src/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export { TimerCreatedEvent } from "./history/timercreatedevent";
export { TimerFiredEvent } from "./history/timerfiredevent";

export { ITaskMethods } from "./tasks/itaskmethods";
export { Task } from "./tasks/task";
export { SingleTask } from "./tasks/task";
export { TaskFactory } from "./tasks/taskfactory";
export { TaskFilter } from "./tasks/taskfilter";
export { TaskSet } from "./tasks/taskset";
Expand Down
18 changes: 9 additions & 9 deletions src/durableorchestrationcontext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EntityId, ITaskMethods, RetryOptions, Task, TimerTask } from "./classes";
import { EntityId, ITaskMethods, RetryOptions, SingleTask, TimerTask } from "./classes";
import { TokenSource } from "./tokensource";

/**
Expand Down Expand Up @@ -65,7 +65,7 @@ export class DurableOrchestrationContext {
* @returns A Durable Task that completes when the called activity
* function completes or fails.
*/
public callActivity(name: string, input?: unknown): Task {
public callActivity(name: string, input?: unknown): SingleTask {
throw new Error("This is a placeholder.");
}

Expand All @@ -78,7 +78,7 @@ export class DurableOrchestrationContext {
* @param input The JSON-serializable input to pass to the activity
* function.
*/
public callActivityWithRetry(name: string, retryOptions: RetryOptions, input?: unknown): Task {
public callActivityWithRetry(name: string, retryOptions: RetryOptions, input?: unknown): SingleTask {
throw new Error("This is a placeholder.");
}

Expand All @@ -90,7 +90,7 @@ export class DurableOrchestrationContext {
* @param operationName The name of the operation.
* @param operationInput The input for the operation.
*/
public callEntity(entityId: EntityId, operationName: string, operationInput?: unknown): Task {
public callEntity(entityId: EntityId, operationName: string, operationInput?: unknown): SingleTask {
throw new Error("This is a placeholder.");
}

Expand All @@ -104,7 +104,7 @@ export class DurableOrchestrationContext {
* If `instanceId` is not specified, the extension will generate an id in
* the format `<calling orchestrator instance ID>:<#>`
*/
public callSubOrchestrator(name: string, input?: unknown, instanceId?: string): Task {
public callSubOrchestrator(name: string, input?: unknown, instanceId?: string): SingleTask {
throw new Error("This is a placeholder.");
}

Expand All @@ -123,7 +123,7 @@ export class DurableOrchestrationContext {
retryOptions: RetryOptions,
input?: unknown,
instanceId?: string)
: Task {
: SingleTask {
throw new Error("This is a placeholder.");
}

Expand All @@ -137,7 +137,7 @@ export class DurableOrchestrationContext {
uri: string,
content?: string | object,
headers?: { [key: string]: string },
tokenSource?: TokenSource): Task {
tokenSource?: TokenSource): SingleTask {
throw new Error("This is a placeholder");
}

Expand All @@ -146,7 +146,7 @@ export class DurableOrchestrationContext {
*
* @param The JSON-serializable data to re-initialize the instance with.
*/
public continueAsNew(input: unknown): Task {
public continueAsNew(input: unknown): SingleTask {
throw new Error("This is a placeholder.");
}

Expand Down Expand Up @@ -213,7 +213,7 @@ export class DurableOrchestrationContext {
* External clients can raise events to a waiting orchestration instance
* using [[raiseEvent]].
*/
public waitForExternalEvent(name: string): Task {
public waitForExternalEvent(name: string): SingleTask {
throw new Error("This is a placeholder.");
}
}
28 changes: 14 additions & 14 deletions src/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { CallActivityAction, CallActivityWithRetryAction, CallEntityAction, Call
CreateTimerAction, DurableHttpRequest, DurableLock, DurableOrchestrationBindingInfo, EntityId,
EventRaisedEvent, EventSentEvent, ExternalEventType, GuidManager, HistoryEvent, HistoryEventType,
IAction, IOrchestrationFunctionContext, LockState, OrchestratorState, RequestMessage, ResponseMessage,
RetryOptions, SubOrchestrationInstanceCompletedEvent, SubOrchestrationInstanceCreatedEvent,
SubOrchestrationInstanceFailedEvent, Task, TaskCompletedEvent, TaskFactory, TaskFailedEvent, TaskFilter,
RetryOptions, SingleTask, SubOrchestrationInstanceCompletedEvent, SubOrchestrationInstanceCreatedEvent,
SubOrchestrationInstanceFailedEvent, TaskCompletedEvent, TaskFactory, TaskFailedEvent, TaskFilter,
TaskScheduledEvent, TaskSet, TimerCreatedEvent, TimerFiredEvent, TimerTask,
Utils, WaitForExternalEventAction,
} from "./classes";
import { DurableError } from "./durableerror";
import { OrchestrationFailureError } from "./orchestrationfailureerror";
import { CompletedTask, TaskBase } from "./tasks/taskinterfaces";
import { CompletedTask, Task } from "./tasks/taskinterfaces";
import { TokenSource } from "./tokensource";

/** @hidden */
Expand Down Expand Up @@ -83,7 +83,7 @@ export class Orchestrator {
// Setup
const gen = this.fn(context);
const actions: IAction[][] = [];
let partialResult: TaskBase;
let partialResult: Task;

try {
// First execution, we have not yet "yielded" any of the tasks.
Expand Down Expand Up @@ -113,7 +113,7 @@ export class Orchestrator {
}
}

partialResult = g.value as TaskBase;
partialResult = g.value as Task;
const newActions = partialResult.yieldNewActions();
if (newActions && newActions.length > 0) {
actions.push(newActions);
Expand Down Expand Up @@ -201,7 +201,7 @@ export class Orchestrator {
}
}

private callActivity(state: HistoryEvent[], name: string, input?: unknown): Task {
private callActivity(state: HistoryEvent[], name: string, input?: unknown): SingleTask {
const newAction = new CallActivityAction(name, input);

const taskScheduled = this.findTaskScheduled(state, name);
Expand Down Expand Up @@ -240,7 +240,7 @@ export class Orchestrator {
name: string,
retryOptions: RetryOptions,
input?: unknown)
: Task {
: SingleTask {
const newAction = new CallActivityWithRetryAction(name, retryOptions, input);

for (let attempt = 1; attempt <= retryOptions.maxNumberOfAttempts; attempt++) {
Expand Down Expand Up @@ -274,7 +274,7 @@ export class Orchestrator {
return TaskFactory.UncompletedTask(newAction);
}

private callEntity(state: HistoryEvent[], entityId: EntityId, operationName: string, operationInput: unknown): Task {
private callEntity(state: HistoryEvent[], entityId: EntityId, operationName: string, operationInput: unknown): SingleTask {
const newAction = new CallEntityAction(entityId, operationName, operationInput);

const schedulerId = EntityId.getSchedulerIdFromEntityId(entityId);
Expand All @@ -299,7 +299,7 @@ export class Orchestrator {
);
}

private callSubOrchestrator(state: HistoryEvent[], name: string, input?: unknown, instanceId?: string): Task {
private callSubOrchestrator(state: HistoryEvent[], name: string, input?: unknown, instanceId?: string): SingleTask {
if (!name) {
throw new Error("A sub-orchestration function name must be provided when attempting to create a suborchestration");
}
Expand Down Expand Up @@ -343,7 +343,7 @@ export class Orchestrator {
retryOptions: RetryOptions,
input?: unknown,
instanceId?: string)
: Task {
: SingleTask {
if (!name) {
throw new Error("A sub-orchestration function name must be provided when attempting to create a suborchestration");
}
Expand Down Expand Up @@ -441,7 +441,7 @@ export class Orchestrator {
}
}

private continueAsNew(state: HistoryEvent[], input: unknown): Task {
private continueAsNew(state: HistoryEvent[], input: unknown): SingleTask {
const newAction = new ContinueAsNewAction(input);

return TaskFactory.UncompletedTask(
Expand Down Expand Up @@ -530,7 +530,7 @@ export class Orchestrator {
this.customStatus = customStatusObject;
}

private waitForExternalEvent(state: HistoryEvent[], name: string): Task {
private waitForExternalEvent(state: HistoryEvent[], name: string): SingleTask {
const newAction = new WaitForExternalEventAction(name, ExternalEventType.ExternalEvent);

const eventRaised = this.findEventRaised(state, name);
Expand All @@ -550,7 +550,7 @@ export class Orchestrator {
}
}

private all(state: HistoryEvent[], tasks: TaskBase[]): TaskSet {
private all(state: HistoryEvent[], tasks: Task[]): TaskSet {
let maxCompletionIndex: number | undefined;
const errors: Error[] = [];
const results: Array<unknown> = [];
Expand Down Expand Up @@ -583,7 +583,7 @@ export class Orchestrator {
}
}

private any(state: HistoryEvent[], tasks: TaskBase[]): TaskSet {
private any(state: HistoryEvent[], tasks: Task[]): TaskSet {
if (!tasks || tasks.length === 0) {
throw new Error("At least one yieldable task must be provided to wait for.");
}
Expand Down
6 changes: 3 additions & 3 deletions src/tasks/itaskmethods.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Task } from "./task";
import { SingleTask } from "./task";
import { TaskSet } from "./taskset";

/**
Expand All @@ -11,11 +11,11 @@ export interface ITaskMethods {
* array containing the results of all [[Task]]s passed to it. It returns
* when all of the [[Task]] instances have completed.
*/
all: (tasks: Task[]) => TaskSet;
all: (tasks: SingleTask[]) => TaskSet;

/**
* Similar to Promise.race. When called with `yield` or `return`, returns
* the first [[Task]] instance to complete.
*/
any: (tasks: Task[]) => TaskSet;
any: (tasks: SingleTask[]) => TaskSet;
}
6 changes: 3 additions & 3 deletions src/tasks/task.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IAction } from "../classes";
import { TaskBase } from "./taskinterfaces";
import { Task } from "./taskinterfaces";

/**
* Represents some pending action. Similar to a native JavaScript promise in
Expand All @@ -10,7 +10,7 @@ import { TaskBase } from "./taskinterfaces";
* [[DurableOrchestrationContext]] operation is not called with `yield`. They
* are useful for parallelization and timeout operations in conjunction with
* Task.all and Task.any.
*
*
* We discourage the usage of `instanceof`-style guards on this type,
* as it is subject to change in the future.
*
Expand All @@ -36,7 +36,7 @@ import { TaskBase } from "./taskinterfaces";
* return firstDone.result;
* ```
*/
export class Task implements TaskBase {
export class SingleTask implements Task {
/**
* @hidden
* Used to keep track of how many times the task has been yielded to avoid
Expand Down
22 changes: 11 additions & 11 deletions src/tasks/taskfactory.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { CreateTimerAction, IAction } from "../classes";
import { Task} from "./task";
import { TaskBase} from "./taskinterfaces";
import { SingleTask } from "./task";
import { Task } from "./taskinterfaces";
import { TaskSet } from "./taskset";
import { TimerTask } from "./timertask";

/** @hidden */
export class TaskFactory {
public static UncompletedTask(action: IAction): Task {
return new Task(false, false, action);
public static UncompletedTask(action: IAction): SingleTask {
return new SingleTask(false, false, action);
}

public static SuccessfulTask(action: IAction, result: unknown, timestamp: Date, id: number, completedHistoryEventIndex: number): Task {
return new Task(
public static SuccessfulTask(action: IAction, result: unknown, timestamp: Date, id: number, completedHistoryEventIndex: number): SingleTask {
return new SingleTask(
true,
false,
action,
Expand All @@ -23,8 +23,8 @@ export class TaskFactory {
);
}

public static FailedTask(action: IAction, reason: string | undefined, timestamp: Date, id: number, completedHistoryEventIndex: number, exception: Error): Task {
return new Task(
public static FailedTask(action: IAction, reason: string | undefined, timestamp: Date, id: number, completedHistoryEventIndex: number, exception: Error): SingleTask {
return new SingleTask(
true,
true,
action,
Expand All @@ -44,7 +44,7 @@ export class TaskFactory {
return new TimerTask(false, action);
}

public static SuccessfulTaskSet(tasks: TaskBase[], completionIndex: number, result: unknown): TaskSet {
public static SuccessfulTaskSet(tasks: Task[], completionIndex: number, result: unknown): TaskSet {
return new TaskSet(
true,
false,
Expand All @@ -55,7 +55,7 @@ export class TaskFactory {
);
}

public static FailedTaskSet(tasks: TaskBase[], completionIndex: number, exception: Error): TaskSet {
public static FailedTaskSet(tasks: Task[], completionIndex: number, exception: Error): TaskSet {
return new TaskSet(
true,
true,
Expand All @@ -66,7 +66,7 @@ export class TaskFactory {
);
}

public static UncompletedTaskSet(tasks: TaskBase[]): TaskSet {
public static UncompletedTaskSet(tasks: Task[]): TaskSet {
return new TaskSet(
false,
false,
Expand Down
30 changes: 15 additions & 15 deletions src/tasks/taskfilter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Task } from "./task";
import { CompletedTask, FailedTask, SuccessfulTask, TaskBase, UncompletedTask } from "./taskinterfaces";
import { SingleTask } from "./task";
import { CompletedTask, FailedTask, SuccessfulTask, Task, UncompletedTask } from "./taskinterfaces";
import { TaskSet } from "./taskset";

/** @hidden */
Expand All @@ -10,35 +10,35 @@ export class TaskFilter {
return 0;
}

public static isYieldable(task: any): task is TaskBase {
const taskBase = task as TaskBase;
return taskBase
&& taskBase.isCompleted !== undefined
&& taskBase.isFaulted !== undefined
&& taskBase.yieldNewActions !== undefined;
public static isYieldable(task: any): task is Task {
const ATask = task as Task;
return ATask
&& ATask.isCompleted !== undefined
&& ATask.isFaulted !== undefined
&& ATask.yieldNewActions !== undefined;
}

public static isSingleTask(task: TaskBase): task is Task {
return (task instanceof Task);
public static isSingleTask(task: Task): task is SingleTask {
return (task instanceof SingleTask);
}

public static isTaskSet(task: TaskBase): task is TaskSet {
public static isTaskSet(task: Task): task is TaskSet {
return (task instanceof TaskSet);
}

public static isCompletedTask(task: TaskBase): task is CompletedTask {
public static isCompletedTask(task: Task): task is CompletedTask {
return task.isCompleted;
}

public static isUncompletedTask(task: TaskBase): task is UncompletedTask {
public static isUncompletedTask(task: Task): task is UncompletedTask {
return task.isCompleted === false;
}

public static isSuccessfulTask(task: TaskBase): task is SuccessfulTask {
public static isSuccessfulTask(task: Task): task is SuccessfulTask {
return task.isCompleted === true && task.isFaulted === false;
}

public static isFailedTask(task: TaskBase): task is FailedTask {
public static isFailedTask(task: Task): task is FailedTask {
return task.isCompleted === true && task.isFaulted === true;
}
}
6 changes: 3 additions & 3 deletions src/tasks/taskinterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import { IAction } from "../classes";

// Base interfaces
/** @hidden */
export interface TaskBase {
export interface Task {
readonly isCompleted: boolean;
readonly isFaulted: boolean;
yieldNewActions(): IAction[];
}

/** @hidden */
export interface UncompletedTask extends TaskBase {
export interface UncompletedTask extends Task {
readonly isCompleted: false;
readonly isFaulted: false;
}

/** @hidden */
export interface CompletedTask extends TaskBase {
export interface CompletedTask extends Task {
readonly completionIndex: number;
readonly isCompleted: true;
readonly result: unknown | undefined;
Expand Down
Loading