Skip to content

Commit

Permalink
Add keep_latest
Browse files Browse the repository at this point in the history
  • Loading branch information
beerinho committed Apr 26, 2024
1 parent 01a2f61 commit e5bc74e
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/lib/handlers/keep_latest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Handler } from './types';

type Handle = ReturnType<Handler>;
type Fn = Parameters<Handle>[0];
type Utils = Parameters<Handle>[1];

const handler = (() => {
let is_running = false;
let queue:
| {
fn: Fn;
utils: Utils;
}
| undefined;

const handle: Handle = async (fn: () => void, utils) => {
if (is_running) {
queue?.utils.abort_controller.abort();
queue = {
fn,
utils,
};
return;
}
is_running = true;

try {
fn();
await utils.promise;
} catch {
/** empty */
}

const next = queue;
is_running = false;
queue = undefined;
if (next) {
handle(next.fn, next.utils);
}
};
return handle;
}) satisfies Handler;

export default handler;
2 changes: 2 additions & 0 deletions src/lib/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { writable } from 'svelte/store';
import default_handler from './handlers/default';
import drop from './handlers/drop';
import enqueue from './handlers/enqueue';
import keep_latest from './handlers/keep_latest';
import restart from './handlers/restart';

const handlers = {
default: default_handler,
drop,
enqueue,
keep_latest,
restart,
} as const;

Expand Down
65 changes: 65 additions & 0 deletions src/lib/tests/components/keep_latest.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script lang="ts">
import { task, type SvelteConcurrencyUtils } from '../../task';
export let fn: (
args: number,
utils: SvelteConcurrencyUtils,
) => Promise<unknown> | AsyncGenerator<unknown, unknown, unknown>;
export let return_value: (value: unknown) => void = () => {};
export let argument = 0;
const default_task = task.keep_latest(fn);
const options_task = task(fn, { kind: 'keep_latest' });
let latest_task_instance: ReturnType<typeof default_task.perform>;
let latest_options_task_instance: ReturnType<typeof options_task.perform>;
</script>

<button
data-testid="perform-default"
on:click={async () => {
latest_task_instance = default_task.perform(argument);
return_value(await latest_task_instance);
}}>perform</button
>

<button
data-testid="perform-options"
on:click={async () => {
latest_options_task_instance = options_task.perform(argument);
return_value(await latest_options_task_instance);
}}>perform options</button
>

<button
data-testid="cancel-default"
on:click={() => {
default_task.cancelAll();
}}>cancel</button
>

<button
data-testid="cancel-options"
on:click={() => {
options_task.cancelAll();
}}>cancel options</button
>

<button
data-testid="cancel-default-last"
on:click={() => {
if (latest_task_instance) {
latest_task_instance.cancel();
}
}}>cancel last instance</button
>

<button
data-testid="cancel-options-last"
on:click={() => {
if (latest_options_task_instance) {
latest_options_task_instance.cancel();
}
}}>cancel last options instance</button
>
33 changes: 33 additions & 0 deletions src/lib/tests/task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Default from './components/default.svelte';
import Enqueue from './components/enqueue.svelte';
import Drop from './components/drop.svelte';
import Restart from './components/restart.svelte';
import KeepLatest from './components/keep_latest.svelte';

function wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
Expand All @@ -21,6 +22,7 @@ describe.each([
{ component: Default, name: 'default' },
{ component: Enqueue, name: 'enqueue' },
{ component: Drop, name: 'drop' },
{ component: KeepLatest, name: 'keep_latest' },
{ component: Restart, name: 'restart' },
])('task - basic functionality $name', ({ component }) => {
all_options((selector) => {
Expand Down Expand Up @@ -327,6 +329,37 @@ describe("task - specific functionality 'drop'", () => {
});
});

describe("task - specific functionality 'keep_latest'", () => {
all_options((selector) => {
it('completes only the first and the last time if multiple instances are created in between', async () => {
let finished = 0;
const fn = vi.fn(async function* () {
await wait(50);
yield;
finished++;
});
const { getByTestId } = render(KeepLatest, {
fn,
});
const perform = getByTestId(`perform-${selector}`);
perform.click();
perform.click();
perform.click();
perform.click();
await vi.waitFor(() => {
expect(fn).toHaveBeenCalledTimes(2);
});
await vi.waitFor(() => {
expect(finished).toBe(2);
});
perform.click();
await vi.waitFor(() => {
expect(finished).toBe(3);
});
});
});
});

describe("task - specific functionality 'restart'", () => {
all_options((selector) => {
it('completes only `max` time if performed when other instances are already running', async () => {
Expand Down
36 changes: 36 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@
return param;
});
const latest_log = task.keep_latest(async (param: number) => {
await new Promise((r) => setTimeout(r, 2000));
return param;
});
let hidden = false;
let x;
let numbers: number[] = [];
</script>

<fieldset>
Expand Down Expand Up @@ -100,6 +107,35 @@
</button>
</fieldset>

<fieldset>
<legend>latest_log</legend>
<pre>{JSON.stringify($latest_log, null, ' ')}</pre>

<button
on:click={() => {
const num = Math.random();
numbers = [...numbers, num];
latest_log.perform(num);
}}
>
Perform
</button>
<button
on:click={() => {
numbers = [];
}}
>
Clear numbers
</button>
<ul>
{#each numbers as number}
<li>
{number}
</li>
{/each}
</ul>
</fieldset>

<button
on:click={() => {
hidden = !hidden;
Expand Down

0 comments on commit e5bc74e

Please sign in to comment.