From 136a08aaeedcb453a76628747736c87dc20755e2 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Tue, 4 Feb 2025 08:26:56 +0100 Subject: [PATCH] feat: allow changing repeating task interval and timeout To allow for changing the frequency and timeout of repeating tasks add `setInterval` and `setTimeout` methods to the task. This lets us repeat a task often to start with, then slow down the repetition once a successful result has occured. --- packages/utils/src/repeating-task.ts | 45 +++++++++++++++++++++ packages/utils/test/repeating-task.spec.ts | 46 ++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/packages/utils/src/repeating-task.ts b/packages/utils/src/repeating-task.ts index a395e0c76c..434388e568 100644 --- a/packages/utils/src/repeating-task.ts +++ b/packages/utils/src/repeating-task.ts @@ -3,7 +3,34 @@ import { anySignal } from 'any-signal' import type { AbortOptions } from '@libp2p/interface' export interface RepeatingTask { + /** + * Update the interval after which the next iteration of the task will run. + * + * This is useful if, for example, you want to retry a task with a short rest + * duration until it succeeds, then periodically after that. + * + * This only affects the next iteration of the task, if it is currently + * running, that run will not be interrupted. + */ + setInterval(ms: number): void + + /** + * Update the amount of time a task will run before the passed abort signal + * will fire. + * + * * This only affects the next iteration of the task, if it is currently + * running, that run will not be interrupted. + */ + setTimeout(ms: number): void + + /** + * Start the task running + */ start(): void + + /** + * Stop the task running + */ stop(): void } @@ -16,6 +43,8 @@ export interface RepeatingTaskOptions { /** * Whether to schedule the task to run immediately + * + * @default false */ runImmediately?: boolean } @@ -54,6 +83,22 @@ export function repeatingTask (fn: (options?: AbortOptions) => void | Promise { + interval = ms + + // maybe reschedule + if (timeout != null) { + clearTimeout(timeout) + timeout = setTimeout(runTask, interval) + } + }, + setTimeout: (ms) => { + if (options == null) { + options = {} + } + + options.timeout = ms + }, start: () => { if (started) { return diff --git a/packages/utils/test/repeating-task.spec.ts b/packages/utils/test/repeating-task.spec.ts index 5f6c87c178..9d9030c948 100644 --- a/packages/utils/test/repeating-task.spec.ts +++ b/packages/utils/test/repeating-task.spec.ts @@ -67,4 +67,50 @@ describe('repeating-task', () => { expect(count).to.be.greaterThan(1) }) + + it('should update the interval of a task', async () => { + let count = 0 + + const task = repeatingTask(() => { + count++ + + if (count === 1) { + task.setInterval(2000) + } + }, 100) + task.start() + + await delay(1000) + + task.stop() + + expect(count).to.equal(1) + }) + + it('should update the timeout of a task', async () => { + let count = 0 + + const task = repeatingTask(async (options) => { + // simulate a delay + await delay(100) + + if (options?.signal?.aborted !== true) { + count++ + } + + if (count === 1) { + // set the task timeout to less than our simulated delay + task.setTimeout(10) + } + }, 100, { + timeout: 500 + }) + task.start() + + await delay(1000) + + task.stop() + + expect(count).to.equal(1) + }) })