@rushstack/operation-graph 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/operation-graph.d.ts +5 -1
- package/lib/OperationExecutionManager.d.ts.map +1 -1
- package/lib/OperationExecutionManager.js +63 -56
- package/lib/OperationExecutionManager.js.map +1 -1
- package/lib/WatchLoop.d.ts +5 -1
- package/lib/WatchLoop.d.ts.map +1 -1
- package/lib/WatchLoop.js +38 -31
- package/lib/WatchLoop.js.map +1 -1
- package/package.json +1 -1
|
@@ -533,7 +533,6 @@ export declare class Stopwatch {
|
|
|
533
533
|
*/
|
|
534
534
|
export declare class WatchLoop implements IWatchLoopState {
|
|
535
535
|
private readonly _options;
|
|
536
|
-
private readonly _outerSignals;
|
|
537
536
|
private _abortController;
|
|
538
537
|
private _isRunning;
|
|
539
538
|
private _runRequested;
|
|
@@ -569,6 +568,11 @@ export declare class WatchLoop implements IWatchLoopState {
|
|
|
569
568
|
* Resets the abort signal and run request state.
|
|
570
569
|
*/
|
|
571
570
|
private _reset;
|
|
571
|
+
/**
|
|
572
|
+
* Runs a single iteration of the loop.
|
|
573
|
+
* @returns The status of the iteration.
|
|
574
|
+
*/
|
|
575
|
+
private _runIterationAsync;
|
|
572
576
|
}
|
|
573
577
|
|
|
574
578
|
export { }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationExecutionManager.d.ts","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":";AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,KAAK,EAA4B,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IAEpB,UAAU,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,qBAAa,yBAAyB;IACpC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IACvE;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAE7B,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC;IAqCrD;;;OAGG;IACU,YAAY,CAAC,gBAAgB,EAAE,0BAA0B,GAAG,OAAO,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"OperationExecutionManager.d.ts","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":";AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,KAAK,EAA4B,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IAEpB,UAAU,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,qBAAa,yBAAyB;IACpC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IACvE;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAE7B,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC;IAqCrD;;;OAGG;IACU,YAAY,CAAC,gBAAgB,EAAE,0BAA0B,GAAG,OAAO,CAAC,eAAe,CAAC;CAqHlG"}
|
|
@@ -67,64 +67,71 @@ class OperationExecutionManager {
|
|
|
67
67
|
}
|
|
68
68
|
terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`);
|
|
69
69
|
const workQueueAbortController = new AbortController();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
groupRecord.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
70
|
+
const abortHandler = () => workQueueAbortController.abort();
|
|
71
|
+
abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
72
|
+
try {
|
|
73
|
+
const workQueue = new WorkQueue_1.WorkQueue(workQueueAbortController.signal);
|
|
74
|
+
const executionContext = {
|
|
75
|
+
terminal,
|
|
76
|
+
abortSignal,
|
|
77
|
+
requestRun,
|
|
78
|
+
queueWork: (workFn, priority) => {
|
|
79
|
+
return workQueue.pushAsync(workFn, priority);
|
|
80
|
+
},
|
|
81
|
+
beforeExecute: (operation) => {
|
|
82
|
+
// Initialize group if uninitialized and log the group name
|
|
83
|
+
const { groupName } = operation;
|
|
84
|
+
const groupRecord = groupName
|
|
85
|
+
? groupRecords.get(groupName)
|
|
86
|
+
: undefined;
|
|
87
|
+
if (groupRecord && !startedGroups.has(groupRecord)) {
|
|
88
|
+
startedGroups.add(groupRecord);
|
|
89
|
+
groupRecord.startTimer();
|
|
90
|
+
terminal.writeLine(` ---- ${groupRecord.name} started ---- `);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
afterExecute: (operation, state) => {
|
|
94
|
+
const { groupName } = operation;
|
|
95
|
+
const groupRecord = groupName
|
|
96
|
+
? groupRecords.get(groupName)
|
|
97
|
+
: undefined;
|
|
98
|
+
if (groupRecord) {
|
|
99
|
+
groupRecord.setOperationAsComplete(operation, state);
|
|
100
|
+
}
|
|
101
|
+
if (state.status === OperationStatus_1.OperationStatus.Failure) {
|
|
102
|
+
// This operation failed. Mark it as such and all reachable dependents as blocked.
|
|
103
|
+
// Failed operations get reported, even if silent.
|
|
104
|
+
// Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure.
|
|
105
|
+
const message = state.error?.message;
|
|
106
|
+
if (message) {
|
|
107
|
+
terminal.writeErrorLine(message);
|
|
108
|
+
}
|
|
109
|
+
hasReportedFailures = true;
|
|
110
|
+
}
|
|
111
|
+
// Log out the group name and duration if it is the last operation in the group
|
|
112
|
+
if (groupRecord?.finished && !finishedGroups.has(groupRecord)) {
|
|
113
|
+
finishedGroups.add(groupRecord);
|
|
114
|
+
const finishedLoggingWord = groupRecord.hasFailures
|
|
115
|
+
? 'encountered an error'
|
|
116
|
+
: groupRecord.hasCancellations
|
|
117
|
+
? 'cancelled'
|
|
118
|
+
: 'finished';
|
|
119
|
+
terminal.writeLine(` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- `);
|
|
106
120
|
}
|
|
107
|
-
hasReportedFailures = true;
|
|
108
|
-
}
|
|
109
|
-
// Log out the group name and duration if it is the last operation in the group
|
|
110
|
-
if (groupRecord?.finished && !finishedGroups.has(groupRecord)) {
|
|
111
|
-
finishedGroups.add(groupRecord);
|
|
112
|
-
const finishedLoggingWord = groupRecord.hasFailures
|
|
113
|
-
? 'encountered an error'
|
|
114
|
-
: groupRecord.hasCancellations
|
|
115
|
-
? 'cancelled'
|
|
116
|
-
: 'finished';
|
|
117
|
-
terminal.writeLine(` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- `);
|
|
118
121
|
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
};
|
|
123
|
+
const workQueuePromise = node_core_library_1.Async.forEachAsync(workQueue, (workFn) => workFn(), {
|
|
124
|
+
concurrency: maxParallelism
|
|
125
|
+
});
|
|
126
|
+
await Promise.all(this._operations.map((record) => record._executeAsync(executionContext)));
|
|
127
|
+
// Terminate queue execution.
|
|
128
|
+
workQueueAbortController.abort();
|
|
129
|
+
await workQueuePromise;
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
// Cleanup resources
|
|
133
|
+
abortSignal.removeEventListener('abort', abortHandler);
|
|
134
|
+
}
|
|
128
135
|
const finalStatus = this._trackedOperationCount === 0
|
|
129
136
|
? OperationStatus_1.OperationStatus.NoOp
|
|
130
137
|
: abortSignal.aborted
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OperationExecutionManager.js","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Async } from '@rushstack/node-core-library';\nimport type { ITerminal } from '@rushstack/terminal';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { IExecuteOperationContext, Operation } from './Operation';\nimport { OperationGroupRecord } from './OperationGroupRecord';\nimport { OperationStatus } from './OperationStatus';\nimport { calculateCriticalPathLengths } from './calculateCriticalPath';\nimport { WorkQueue } from './WorkQueue';\n\n/**\n * Options for the current run.\n *\n * @beta\n */\nexport interface IOperationExecutionOptions {\n abortSignal: AbortSignal;\n parallelism: number;\n terminal: ITerminal;\n\n requestRun?: (requestor?: string) => void;\n}\n\n/**\n * A class which manages the execution of a set of tasks with interdependencies.\n * Initially, and at the end of each task execution, all unblocked tasks\n * are added to a ready queue which is then executed. This is done continually until all\n * tasks are complete, or prematurely fails if any of the tasks fail.\n *\n * @beta\n */\nexport class OperationExecutionManager {\n /**\n * The set of operations that will be executed\n */\n private readonly _operations: Operation[];\n /**\n * Group records are metadata-only entities used for tracking the start and end of a set of related tasks.\n * This is the only extent to which the operation graph is aware of Heft phases.\n */\n private readonly _groupRecordByName: Map<string, OperationGroupRecord>;\n /**\n * The total number of non-silent operations in the graph.\n * Silent operations are generally used to simplify the construction of the graph.\n */\n private readonly _trackedOperationCount: number;\n\n public constructor(operations: ReadonlySet<Operation>) {\n const groupRecordByName: Map<string, OperationGroupRecord> = new Map();\n this._groupRecordByName = groupRecordByName;\n\n let trackedOperationCount: number = 0;\n for (const operation of operations) {\n const { groupName } = operation;\n let group: OperationGroupRecord | undefined = undefined;\n if (groupName && !(group = groupRecordByName.get(groupName))) {\n group = new OperationGroupRecord(groupName);\n groupRecordByName.set(groupName, group);\n }\n\n group?.addOperation(operation);\n\n if (!operation.runner?.silent) {\n // Only count non-silent operations\n trackedOperationCount++;\n }\n }\n\n this._trackedOperationCount = trackedOperationCount;\n\n this._operations = calculateCriticalPathLengths(operations);\n\n for (const consumer of operations) {\n for (const dependency of consumer.dependencies) {\n if (!operations.has(dependency)) {\n throw new Error(\n `Operation ${JSON.stringify(consumer.name)} declares a dependency on operation ` +\n `${JSON.stringify(dependency.name)} that is not in the set of operations to execute.`\n );\n }\n }\n }\n }\n\n /**\n * Executes all operations which have been registered, returning a promise which is resolved when all the\n * operations are completed successfully, or rejects when any operation fails.\n */\n public async executeAsync(executionOptions: IOperationExecutionOptions): Promise<OperationStatus> {\n let hasReportedFailures: boolean = false;\n\n const { abortSignal, parallelism, terminal, requestRun } = executionOptions;\n\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n const startedGroups: Set<OperationGroupRecord> = new Set();\n const finishedGroups: Set<OperationGroupRecord> = new Set();\n\n const maxParallelism: number = Math.min(this._operations.length, parallelism);\n const groupRecords: Map<string, OperationGroupRecord> = this._groupRecordByName;\n for (const groupRecord of groupRecords.values()) {\n groupRecord.reset();\n }\n\n for (const operation of this._operations) {\n operation.reset();\n }\n\n terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`);\n\n const workQueueAbortController: AbortController = new AbortController();\n abortSignal.addEventListener('abort', () => workQueueAbortController.abort(), { once: true });\n const workQueue: WorkQueue = new WorkQueue(workQueueAbortController.signal);\n\n const executionContext: IExecuteOperationContext = {\n terminal,\n abortSignal,\n\n requestRun,\n\n queueWork: (workFn: () => Promise<OperationStatus>, priority: number): Promise<OperationStatus> => {\n return workQueue.pushAsync(workFn, priority);\n },\n\n beforeExecute: (operation: Operation): void => {\n // Initialize group if uninitialized and log the group name\n const { groupName } = operation;\n const groupRecord: OperationGroupRecord | undefined = groupName\n ? groupRecords.get(groupName)\n : undefined;\n if (groupRecord && !startedGroups.has(groupRecord)) {\n startedGroups.add(groupRecord);\n groupRecord.startTimer();\n terminal.writeLine(` ---- ${groupRecord.name} started ---- `);\n }\n },\n\n afterExecute: (operation: Operation, state: IOperationState): void => {\n const { groupName } = operation;\n const groupRecord: OperationGroupRecord | undefined = groupName\n ? groupRecords.get(groupName)\n : undefined;\n if (groupRecord) {\n groupRecord.setOperationAsComplete(operation, state);\n }\n\n if (state.status === OperationStatus.Failure) {\n // This operation failed. Mark it as such and all reachable dependents as blocked.\n // Failed operations get reported, even if silent.\n // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure.\n const message: string | undefined = state.error?.message;\n if (message) {\n terminal.writeErrorLine(message);\n }\n hasReportedFailures = true;\n }\n\n // Log out the group name and duration if it is the last operation in the group\n if (groupRecord?.finished && !finishedGroups.has(groupRecord)) {\n finishedGroups.add(groupRecord);\n const finishedLoggingWord: string = groupRecord.hasFailures\n ? 'encountered an error'\n : groupRecord.hasCancellations\n ? 'cancelled'\n : 'finished';\n terminal.writeLine(\n ` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- `\n );\n }\n }\n };\n\n const workQueuePromise: Promise<void> = Async.forEachAsync(\n workQueue,\n (workFn: () => Promise<void>) => workFn(),\n {\n concurrency: maxParallelism\n }\n );\n\n await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext)));\n\n // Terminate queue execution.\n workQueueAbortController.abort();\n await workQueuePromise;\n\n const finalStatus: OperationStatus =\n this._trackedOperationCount === 0\n ? OperationStatus.NoOp\n : abortSignal.aborted\n ? OperationStatus.Aborted\n : hasReportedFailures\n ? OperationStatus.Failure\n : OperationStatus.Success;\n\n return finalStatus;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"OperationExecutionManager.js","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Async } from '@rushstack/node-core-library';\nimport type { ITerminal } from '@rushstack/terminal';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { IExecuteOperationContext, Operation } from './Operation';\nimport { OperationGroupRecord } from './OperationGroupRecord';\nimport { OperationStatus } from './OperationStatus';\nimport { calculateCriticalPathLengths } from './calculateCriticalPath';\nimport { WorkQueue } from './WorkQueue';\n\n/**\n * Options for the current run.\n *\n * @beta\n */\nexport interface IOperationExecutionOptions {\n abortSignal: AbortSignal;\n parallelism: number;\n terminal: ITerminal;\n\n requestRun?: (requestor?: string) => void;\n}\n\n/**\n * A class which manages the execution of a set of tasks with interdependencies.\n * Initially, and at the end of each task execution, all unblocked tasks\n * are added to a ready queue which is then executed. This is done continually until all\n * tasks are complete, or prematurely fails if any of the tasks fail.\n *\n * @beta\n */\nexport class OperationExecutionManager {\n /**\n * The set of operations that will be executed\n */\n private readonly _operations: Operation[];\n /**\n * Group records are metadata-only entities used for tracking the start and end of a set of related tasks.\n * This is the only extent to which the operation graph is aware of Heft phases.\n */\n private readonly _groupRecordByName: Map<string, OperationGroupRecord>;\n /**\n * The total number of non-silent operations in the graph.\n * Silent operations are generally used to simplify the construction of the graph.\n */\n private readonly _trackedOperationCount: number;\n\n public constructor(operations: ReadonlySet<Operation>) {\n const groupRecordByName: Map<string, OperationGroupRecord> = new Map();\n this._groupRecordByName = groupRecordByName;\n\n let trackedOperationCount: number = 0;\n for (const operation of operations) {\n const { groupName } = operation;\n let group: OperationGroupRecord | undefined = undefined;\n if (groupName && !(group = groupRecordByName.get(groupName))) {\n group = new OperationGroupRecord(groupName);\n groupRecordByName.set(groupName, group);\n }\n\n group?.addOperation(operation);\n\n if (!operation.runner?.silent) {\n // Only count non-silent operations\n trackedOperationCount++;\n }\n }\n\n this._trackedOperationCount = trackedOperationCount;\n\n this._operations = calculateCriticalPathLengths(operations);\n\n for (const consumer of operations) {\n for (const dependency of consumer.dependencies) {\n if (!operations.has(dependency)) {\n throw new Error(\n `Operation ${JSON.stringify(consumer.name)} declares a dependency on operation ` +\n `${JSON.stringify(dependency.name)} that is not in the set of operations to execute.`\n );\n }\n }\n }\n }\n\n /**\n * Executes all operations which have been registered, returning a promise which is resolved when all the\n * operations are completed successfully, or rejects when any operation fails.\n */\n public async executeAsync(executionOptions: IOperationExecutionOptions): Promise<OperationStatus> {\n let hasReportedFailures: boolean = false;\n\n const { abortSignal, parallelism, terminal, requestRun } = executionOptions;\n\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n const startedGroups: Set<OperationGroupRecord> = new Set();\n const finishedGroups: Set<OperationGroupRecord> = new Set();\n\n const maxParallelism: number = Math.min(this._operations.length, parallelism);\n const groupRecords: Map<string, OperationGroupRecord> = this._groupRecordByName;\n for (const groupRecord of groupRecords.values()) {\n groupRecord.reset();\n }\n\n for (const operation of this._operations) {\n operation.reset();\n }\n\n terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`);\n\n const workQueueAbortController: AbortController = new AbortController();\n const abortHandler: () => void = () => workQueueAbortController.abort();\n abortSignal.addEventListener('abort', abortHandler, { once: true });\n try {\n const workQueue: WorkQueue = new WorkQueue(workQueueAbortController.signal);\n\n const executionContext: IExecuteOperationContext = {\n terminal,\n abortSignal,\n\n requestRun,\n\n queueWork: (workFn: () => Promise<OperationStatus>, priority: number): Promise<OperationStatus> => {\n return workQueue.pushAsync(workFn, priority);\n },\n\n beforeExecute: (operation: Operation): void => {\n // Initialize group if uninitialized and log the group name\n const { groupName } = operation;\n const groupRecord: OperationGroupRecord | undefined = groupName\n ? groupRecords.get(groupName)\n : undefined;\n if (groupRecord && !startedGroups.has(groupRecord)) {\n startedGroups.add(groupRecord);\n groupRecord.startTimer();\n terminal.writeLine(` ---- ${groupRecord.name} started ---- `);\n }\n },\n\n afterExecute: (operation: Operation, state: IOperationState): void => {\n const { groupName } = operation;\n const groupRecord: OperationGroupRecord | undefined = groupName\n ? groupRecords.get(groupName)\n : undefined;\n if (groupRecord) {\n groupRecord.setOperationAsComplete(operation, state);\n }\n\n if (state.status === OperationStatus.Failure) {\n // This operation failed. Mark it as such and all reachable dependents as blocked.\n // Failed operations get reported, even if silent.\n // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure.\n const message: string | undefined = state.error?.message;\n if (message) {\n terminal.writeErrorLine(message);\n }\n hasReportedFailures = true;\n }\n\n // Log out the group name and duration if it is the last operation in the group\n if (groupRecord?.finished && !finishedGroups.has(groupRecord)) {\n finishedGroups.add(groupRecord);\n const finishedLoggingWord: string = groupRecord.hasFailures\n ? 'encountered an error'\n : groupRecord.hasCancellations\n ? 'cancelled'\n : 'finished';\n terminal.writeLine(\n ` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- `\n );\n }\n }\n };\n\n const workQueuePromise: Promise<void> = Async.forEachAsync(\n workQueue,\n (workFn: () => Promise<void>) => workFn(),\n {\n concurrency: maxParallelism\n }\n );\n\n await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext)));\n\n // Terminate queue execution.\n workQueueAbortController.abort();\n await workQueuePromise;\n } finally {\n // Cleanup resources\n abortSignal.removeEventListener('abort', abortHandler);\n }\n\n const finalStatus: OperationStatus =\n this._trackedOperationCount === 0\n ? OperationStatus.NoOp\n : abortSignal.aborted\n ? OperationStatus.Aborted\n : hasReportedFailures\n ? OperationStatus.Failure\n : OperationStatus.Success;\n\n return finalStatus;\n }\n}\n"]}
|
package/lib/WatchLoop.d.ts
CHANGED
|
@@ -40,7 +40,6 @@ export interface IWatchLoopState {
|
|
|
40
40
|
*/
|
|
41
41
|
export declare class WatchLoop implements IWatchLoopState {
|
|
42
42
|
private readonly _options;
|
|
43
|
-
private readonly _outerSignals;
|
|
44
43
|
private _abortController;
|
|
45
44
|
private _isRunning;
|
|
46
45
|
private _runRequested;
|
|
@@ -76,5 +75,10 @@ export declare class WatchLoop implements IWatchLoopState {
|
|
|
76
75
|
* Resets the abort signal and run request state.
|
|
77
76
|
*/
|
|
78
77
|
private _reset;
|
|
78
|
+
/**
|
|
79
|
+
* Runs a single iteration of the loop.
|
|
80
|
+
* @returns The status of the iteration.
|
|
81
|
+
*/
|
|
82
|
+
private _runIterationAsync;
|
|
79
83
|
}
|
|
80
84
|
//# sourceMappingURL=WatchLoop.d.ts.map
|
package/lib/WatchLoop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WatchLoop.d.ts","sourceRoot":"","sources":["../src/WatchLoop.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAEV,OAAO,EAIR,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACnE;;OAEG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B;;OAEG;IACH,YAAY,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,WAAW,IAAI,WAAW,CAAC;IAC/B,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED;;;;GAIG;AACH,qBAAa,SAAU,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;
|
|
1
|
+
{"version":3,"file":"WatchLoop.d.ts","sourceRoot":"","sources":["../src/WatchLoop.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAEV,OAAO,EAIR,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACnE;;OAEG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B;;OAEG;IACH,YAAY,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,WAAW,IAAI,WAAW,CAAC;IAC/B,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED;;;;GAIG;AACH,qBAAa,SAAU,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IAEvD,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,kBAAkB,CAA8B;IACxD,OAAO,CAAC,kBAAkB,CAAgC;gBAEvC,OAAO,EAAE,iBAAiB;IAY7C;;OAEG;IACU,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IA0BpF;;OAEG;IACU,oBAAoB,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBjG;;;OAGG;IACU,WAAW,CAAC,IAAI,GAAE,OAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkFhE;;OAEG;IACI,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAU7C;IAEF;;OAEG;IACH,IAAW,WAAW,IAAI,WAAW,CAEpC;IAED;;OAEG;IACH,OAAO,CAAC,aAAa,CAEnB;IAEF;;OAEG;IACH,OAAO,CAAC,MAAM;IAad;;;OAGG;YACW,kBAAkB;CAiBjC"}
|
package/lib/WatchLoop.js
CHANGED
|
@@ -41,7 +41,6 @@ class WatchLoop {
|
|
|
41
41
|
this._requestRunPromise = new Promise((resolve) => {
|
|
42
42
|
this._resolveRequestRun = resolve;
|
|
43
43
|
});
|
|
44
|
-
this._outerSignals = new WeakSet();
|
|
45
44
|
}
|
|
46
45
|
/**
|
|
47
46
|
* Runs the inner loop until the abort signal is cancelled or a run completes without a new run being requested.
|
|
@@ -50,37 +49,22 @@ class WatchLoop {
|
|
|
50
49
|
if (abortSignal.aborted) {
|
|
51
50
|
return OperationStatus_1.OperationStatus.Aborted;
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Always check the abort signal first, in case it was aborted in the async tick since the last executeAsync() call.
|
|
61
|
-
if (abortSignal.aborted) {
|
|
62
|
-
return OperationStatus_1.OperationStatus.Aborted;
|
|
63
|
-
}
|
|
64
|
-
this._reset();
|
|
65
|
-
this._options.onBeforeExecute();
|
|
66
|
-
try {
|
|
67
|
-
this._isRunning = true;
|
|
68
|
-
result = await this._options.executeAsync(this);
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
if (!(err instanceof node_core_library_1.AlreadyReportedError)) {
|
|
72
|
-
throw err;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
result = OperationStatus_1.OperationStatus.Failure;
|
|
52
|
+
abortSignal.addEventListener('abort', this._abortCurrent, { once: true });
|
|
53
|
+
try {
|
|
54
|
+
let result = OperationStatus_1.OperationStatus.Ready;
|
|
55
|
+
do {
|
|
56
|
+
// Always check the abort signal first, in case it was aborted in the async tick since the last executeAsync() call.
|
|
57
|
+
if (abortSignal.aborted) {
|
|
58
|
+
return OperationStatus_1.OperationStatus.Aborted;
|
|
76
59
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
60
|
+
result = await this._runIterationAsync();
|
|
61
|
+
} while (this._runRequested);
|
|
62
|
+
// Even if the run has finished, if the abort signal was aborted, we should return `Aborted` just in case.
|
|
63
|
+
return abortSignal.aborted ? OperationStatus_1.OperationStatus.Aborted : result;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
abortSignal.removeEventListener('abort', this._abortCurrent);
|
|
67
|
+
}
|
|
84
68
|
}
|
|
85
69
|
/**
|
|
86
70
|
* Runs the inner loop until the abort signal is aborted. Will otherwise wait indefinitely for a new run to be requested.
|
|
@@ -190,6 +174,29 @@ class WatchLoop {
|
|
|
190
174
|
});
|
|
191
175
|
}
|
|
192
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Runs a single iteration of the loop.
|
|
179
|
+
* @returns The status of the iteration.
|
|
180
|
+
*/
|
|
181
|
+
async _runIterationAsync() {
|
|
182
|
+
this._reset();
|
|
183
|
+
this._options.onBeforeExecute();
|
|
184
|
+
try {
|
|
185
|
+
this._isRunning = true;
|
|
186
|
+
return await this._options.executeAsync(this);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
if (!(err instanceof node_core_library_1.AlreadyReportedError)) {
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
return OperationStatus_1.OperationStatus.Failure;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
this._isRunning = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
193
200
|
}
|
|
194
201
|
exports.WatchLoop = WatchLoop;
|
|
195
202
|
//# sourceMappingURL=WatchLoop.js.map
|
package/lib/WatchLoop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WatchLoop.js","sourceRoot":"","sources":["../src/WatchLoop.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { once } from 'node:events';\n\nimport { AlreadyReportedError } from '@rushstack/node-core-library';\n\nimport { OperationStatus } from './OperationStatus';\nimport type {\n IAfterExecuteEventMessage,\n IPCHost,\n CommandMessageFromHost,\n ISyncEventMessage,\n IRequestRunEventMessage\n} from './protocol.types';\n\n/**\n * Callbacks for the watch loop.\n *\n * @beta\n */\nexport interface IWatchLoopOptions {\n /**\n * Callback that performs the core work of a single iteration.\n */\n executeAsync: (state: IWatchLoopState) => Promise<OperationStatus>;\n /**\n * Logging callback immediately before execution occurs.\n */\n onBeforeExecute: () => void;\n /**\n * Logging callback when a run is requested (and hasn't already been).\n */\n onRequestRun: (requestor?: string) => void;\n /**\n * Logging callback when a run is aborted.\n */\n onAbort: () => void;\n}\n\n/**\n * The public API surface of the watch loop, for use in the `executeAsync` callback.\n *\n * @beta\n */\nexport interface IWatchLoopState {\n get abortSignal(): AbortSignal;\n requestRun: (requestor?: string) => void;\n}\n\n/**\n * This class implements a watch loop.\n *\n * @beta\n */\nexport class WatchLoop implements IWatchLoopState {\n private readonly _options: Readonly<IWatchLoopOptions>;\n private readonly _outerSignals: WeakSet<AbortSignal>;\n\n private _abortController: AbortController;\n private _isRunning: boolean;\n private _runRequested: boolean;\n private _requestRunPromise: Promise<string | undefined>;\n private _resolveRequestRun!: (requestor?: string) => void;\n\n public constructor(options: IWatchLoopOptions) {\n this._options = options;\n\n this._abortController = new AbortController();\n this._isRunning = false;\n // Always start as true, so that any requests prior to first run are silenced.\n this._runRequested = true;\n this._requestRunPromise = new Promise<string | undefined>((resolve) => {\n this._resolveRequestRun = resolve;\n });\n\n this._outerSignals = new WeakSet();\n }\n\n /**\n * Runs the inner loop until the abort signal is cancelled or a run completes without a new run being requested.\n */\n public async runUntilStableAsync(abortSignal: AbortSignal): Promise<OperationStatus> {\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n if (!this._outerSignals.has(abortSignal)) {\n // ESLINT: \"Promises must be awaited, end with a call to .catch, end with a call to .then ...\"\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n once(abortSignal, 'abort').finally(this._abortCurrent);\n }\n\n let result: OperationStatus = OperationStatus.Ready;\n\n do {\n // Always check the abort signal first, in case it was aborted in the async tick since the last executeAsync() call.\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n this._reset();\n\n this._options.onBeforeExecute();\n try {\n this._isRunning = true;\n result = await this._options.executeAsync(this);\n } catch (err) {\n if (!(err instanceof AlreadyReportedError)) {\n throw err;\n } else {\n result = OperationStatus.Failure;\n }\n } finally {\n this._isRunning = false;\n }\n } while (this._runRequested);\n\n // Even if the run has finished, if the abort signal was aborted, we should return `Aborted` just in case.\n return abortSignal.aborted ? OperationStatus.Aborted : result;\n }\n\n /**\n * Runs the inner loop until the abort signal is aborted. Will otherwise wait indefinitely for a new run to be requested.\n */\n public async runUntilAbortedAsync(abortSignal: AbortSignal, onWaiting: () => void): Promise<void> {\n if (abortSignal.aborted) {\n return;\n }\n\n const abortPromise: Promise<unknown> = once(abortSignal, 'abort');\n\n // eslint-disable-next-line no-constant-condition\n while (!abortSignal.aborted) {\n await this.runUntilStableAsync(abortSignal);\n\n onWaiting();\n await Promise.race([this._requestRunPromise, abortPromise]);\n }\n }\n\n /**\n * Sets up an IPC handler that will run the inner loop when it receives a \"run\" message from the host.\n * Runs until receiving an \"exit\" message from the host, or aborts early if an unhandled error is thrown.\n */\n public async runIPCAsync(host: IPCHost = process): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n let abortController: AbortController = new AbortController();\n\n let runRequestedFromHost: boolean = true;\n let status: OperationStatus = OperationStatus.Ready;\n\n function requestRunFromHost(requestor?: string): void {\n if (runRequestedFromHost) {\n return;\n }\n\n runRequestedFromHost = true;\n\n const requestRunMessage: IRequestRunEventMessage = {\n event: 'requestRun',\n requestor\n };\n\n host.send!(requestRunMessage);\n }\n\n function sendSync(): void {\n const syncMessage: ISyncEventMessage = {\n event: 'sync',\n status\n };\n host.send!(syncMessage);\n }\n\n host.on('message', async (message: CommandMessageFromHost) => {\n switch (message.command) {\n case 'exit': {\n return resolve();\n }\n\n case 'cancel': {\n if (this._isRunning) {\n abortController.abort();\n abortController = new AbortController();\n // This will terminate the currently executing `runUntilStableAsync` call.\n }\n return;\n }\n\n case 'run': {\n runRequestedFromHost = false;\n\n status = OperationStatus.Executing;\n\n try {\n status = await this.runUntilStableAsync(abortController.signal);\n // ESLINT: \"Promises must be awaited, end with a call to .catch, end with a call to .then ...\"\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._requestRunPromise.finally(requestRunFromHost);\n } catch (err) {\n status = OperationStatus.Failure;\n return reject(err);\n } finally {\n const afterExecuteMessage: IAfterExecuteEventMessage = {\n event: 'after-execute',\n status\n };\n host.send!(afterExecuteMessage);\n }\n return;\n }\n\n case 'sync': {\n return sendSync();\n }\n\n default: {\n return reject(new Error(`Unexpected command from host: ${message}`));\n }\n }\n });\n\n sendSync();\n });\n }\n\n /**\n * Requests that a new run occur.\n */\n public requestRun: (requestor?: string) => void = (requestor?: string) => {\n if (!this._runRequested) {\n this._options.onRequestRun(requestor);\n this._runRequested = true;\n if (this._isRunning) {\n this._options.onAbort();\n this._abortCurrent();\n }\n }\n this._resolveRequestRun(requestor);\n };\n\n /**\n * The abort signal for the current iteration.\n */\n public get abortSignal(): AbortSignal {\n return this._abortController.signal;\n }\n\n /**\n * Cancels the current iteration (if possible).\n */\n private _abortCurrent = (): void => {\n this._abortController.abort();\n };\n\n /**\n * Resets the abort signal and run request state.\n */\n private _reset(): void {\n if (this._abortController.signal.aborted) {\n this._abortController = new AbortController();\n }\n\n if (this._runRequested) {\n this._runRequested = false;\n this._requestRunPromise = new Promise<string | undefined>((resolve) => {\n this._resolveRequestRun = resolve;\n });\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WatchLoop.js","sourceRoot":"","sources":["../src/WatchLoop.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { once } from 'node:events';\n\nimport { AlreadyReportedError } from '@rushstack/node-core-library';\n\nimport { OperationStatus } from './OperationStatus';\nimport type {\n IAfterExecuteEventMessage,\n IPCHost,\n CommandMessageFromHost,\n ISyncEventMessage,\n IRequestRunEventMessage\n} from './protocol.types';\n\n/**\n * Callbacks for the watch loop.\n *\n * @beta\n */\nexport interface IWatchLoopOptions {\n /**\n * Callback that performs the core work of a single iteration.\n */\n executeAsync: (state: IWatchLoopState) => Promise<OperationStatus>;\n /**\n * Logging callback immediately before execution occurs.\n */\n onBeforeExecute: () => void;\n /**\n * Logging callback when a run is requested (and hasn't already been).\n */\n onRequestRun: (requestor?: string) => void;\n /**\n * Logging callback when a run is aborted.\n */\n onAbort: () => void;\n}\n\n/**\n * The public API surface of the watch loop, for use in the `executeAsync` callback.\n *\n * @beta\n */\nexport interface IWatchLoopState {\n get abortSignal(): AbortSignal;\n requestRun: (requestor?: string) => void;\n}\n\n/**\n * This class implements a watch loop.\n *\n * @beta\n */\nexport class WatchLoop implements IWatchLoopState {\n private readonly _options: Readonly<IWatchLoopOptions>;\n\n private _abortController: AbortController;\n private _isRunning: boolean;\n private _runRequested: boolean;\n private _requestRunPromise: Promise<string | undefined>;\n private _resolveRequestRun!: (requestor?: string) => void;\n\n public constructor(options: IWatchLoopOptions) {\n this._options = options;\n\n this._abortController = new AbortController();\n this._isRunning = false;\n // Always start as true, so that any requests prior to first run are silenced.\n this._runRequested = true;\n this._requestRunPromise = new Promise<string | undefined>((resolve) => {\n this._resolveRequestRun = resolve;\n });\n }\n\n /**\n * Runs the inner loop until the abort signal is cancelled or a run completes without a new run being requested.\n */\n public async runUntilStableAsync(abortSignal: AbortSignal): Promise<OperationStatus> {\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n abortSignal.addEventListener('abort', this._abortCurrent, { once: true });\n\n try {\n let result: OperationStatus = OperationStatus.Ready;\n\n do {\n // Always check the abort signal first, in case it was aborted in the async tick since the last executeAsync() call.\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n result = await this._runIterationAsync();\n } while (this._runRequested);\n\n // Even if the run has finished, if the abort signal was aborted, we should return `Aborted` just in case.\n return abortSignal.aborted ? OperationStatus.Aborted : result;\n } finally {\n abortSignal.removeEventListener('abort', this._abortCurrent);\n }\n }\n\n /**\n * Runs the inner loop until the abort signal is aborted. Will otherwise wait indefinitely for a new run to be requested.\n */\n public async runUntilAbortedAsync(abortSignal: AbortSignal, onWaiting: () => void): Promise<void> {\n if (abortSignal.aborted) {\n return;\n }\n\n const abortPromise: Promise<unknown> = once(abortSignal, 'abort');\n\n // eslint-disable-next-line no-constant-condition\n while (!abortSignal.aborted) {\n await this.runUntilStableAsync(abortSignal);\n\n onWaiting();\n await Promise.race([this._requestRunPromise, abortPromise]);\n }\n }\n\n /**\n * Sets up an IPC handler that will run the inner loop when it receives a \"run\" message from the host.\n * Runs until receiving an \"exit\" message from the host, or aborts early if an unhandled error is thrown.\n */\n public async runIPCAsync(host: IPCHost = process): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n let abortController: AbortController = new AbortController();\n\n let runRequestedFromHost: boolean = true;\n let status: OperationStatus = OperationStatus.Ready;\n\n function requestRunFromHost(requestor?: string): void {\n if (runRequestedFromHost) {\n return;\n }\n\n runRequestedFromHost = true;\n\n const requestRunMessage: IRequestRunEventMessage = {\n event: 'requestRun',\n requestor\n };\n\n host.send!(requestRunMessage);\n }\n\n function sendSync(): void {\n const syncMessage: ISyncEventMessage = {\n event: 'sync',\n status\n };\n host.send!(syncMessage);\n }\n\n host.on('message', async (message: CommandMessageFromHost) => {\n switch (message.command) {\n case 'exit': {\n return resolve();\n }\n\n case 'cancel': {\n if (this._isRunning) {\n abortController.abort();\n abortController = new AbortController();\n // This will terminate the currently executing `runUntilStableAsync` call.\n }\n return;\n }\n\n case 'run': {\n runRequestedFromHost = false;\n\n status = OperationStatus.Executing;\n\n try {\n status = await this.runUntilStableAsync(abortController.signal);\n // ESLINT: \"Promises must be awaited, end with a call to .catch, end with a call to .then ...\"\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._requestRunPromise.finally(requestRunFromHost);\n } catch (err) {\n status = OperationStatus.Failure;\n return reject(err);\n } finally {\n const afterExecuteMessage: IAfterExecuteEventMessage = {\n event: 'after-execute',\n status\n };\n host.send!(afterExecuteMessage);\n }\n return;\n }\n\n case 'sync': {\n return sendSync();\n }\n\n default: {\n return reject(new Error(`Unexpected command from host: ${message}`));\n }\n }\n });\n\n sendSync();\n });\n }\n\n /**\n * Requests that a new run occur.\n */\n public requestRun: (requestor?: string) => void = (requestor?: string) => {\n if (!this._runRequested) {\n this._options.onRequestRun(requestor);\n this._runRequested = true;\n if (this._isRunning) {\n this._options.onAbort();\n this._abortCurrent();\n }\n }\n this._resolveRequestRun(requestor);\n };\n\n /**\n * The abort signal for the current iteration.\n */\n public get abortSignal(): AbortSignal {\n return this._abortController.signal;\n }\n\n /**\n * Cancels the current iteration (if possible).\n */\n private _abortCurrent = (): void => {\n this._abortController.abort();\n };\n\n /**\n * Resets the abort signal and run request state.\n */\n private _reset(): void {\n if (this._abortController.signal.aborted) {\n this._abortController = new AbortController();\n }\n\n if (this._runRequested) {\n this._runRequested = false;\n this._requestRunPromise = new Promise<string | undefined>((resolve) => {\n this._resolveRequestRun = resolve;\n });\n }\n }\n\n /**\n * Runs a single iteration of the loop.\n * @returns The status of the iteration.\n */\n private async _runIterationAsync(): Promise<OperationStatus> {\n this._reset();\n\n this._options.onBeforeExecute();\n try {\n this._isRunning = true;\n return await this._options.executeAsync(this);\n } catch (err) {\n if (!(err instanceof AlreadyReportedError)) {\n throw err;\n } else {\n return OperationStatus.Failure;\n }\n } finally {\n this._isRunning = false;\n }\n }\n}\n"]}
|
package/package.json
CHANGED