
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
last-wins-and-cancels-prevs
Advanced tools
 [](https://codecov.io/gh/avfirsov/last-wins-and-cancels-pre
A minimal async queue where only the latest task is executed, all previous are auto-cancelled via AbortController. Supports debounce/throttle, hooks for all task events, and guarantees that .result always reflects the last completed task.
signal.aborted.result: promise resolves only when the last real task completesqueue.result lifecyclequeue.result === undefined — queue is empty, nothing to awaitqueue.result is a promise for the last (not-yet-cancelled) taskqueue.result === undefined — queue is empty againExample:
const queue = new LastWinsAndCancelsPrevious<number>();
console.log(queue.result); // undefined
queue.run(async () => 1);
console.log(typeof queue.result.then); // 'function' — it's a promise
await queue.result; // 1
console.log(queue.result); // undefined (queue is empty again)
import { LastWinsAndCancelsPrevious } from 'last-wins-and-cancels-prevs';
const queue = new LastWinsAndCancelsPrevious<string>({ debounceMs: 300 });
console.log(queue.result) //undefined - the queue is empty
queue.run(signal => fetch('/api?q=first', { signal }).then(r => r.text()));
queue.run(signal => fetch('/api?q=second', { signal }).then(r => r.text()));
queue.run(signal => fetch('/api?q=final', { signal }).then(r => r.text()));
const finalResult = await queue.result;
console.log(finalResult); // result of the last request
<script setup lang="ts">
import { ref, watch, onUnmounted } from 'vue';
import { LastWinsAndCancelsPrevious } from 'last-wins-and-cancels-prevs';
const searchQuery = ref('');
const results = ref<string[]>([]);
const isLoading = ref(false);
// Create queue with 500ms debounce
const queue = new LastWinsAndCancelsPrevious<string[]>({ debounceMs: 500 });
queue.onSeriesStarted(() => {
isLoading.value = true;
});
queue.onAborted(({ isSeriesEnd }) => {
if (isSeriesEnd) isLoading.value = false;
});
queue.onError(({ isSeriesEnd }) => {
if (isSeriesEnd) isLoading.value = false;
queue.onComplete(({ result, isSeriesEnd }) => {
if (isSeriesEnd) {
isLoading.value = false;
if (result) results.value = result;
}
});
watch(searchQuery, (q) => {
queue.run(async (signal) => {
// Simulate API call with abort support
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, { signal });
return await res.json();
});
});
onUnmounted(() => {
queue.abort(); // Cancel any pending request when component unmounts
});
</script>
<template>
<input v-model="searchQuery" placeholder="Search..." />
<span v-if="isLoading">Loading...</span>
<ul>
<li v-for="item in results" :key="item">{{ item }}</li>
</ul>
</template>
const queue = new LastWinsAndCancelsPrevious<number>();
queue.onAborted(({ signal, isSeriesEnd }) => {
console.log('Aborted!', signal, 'isSeriesEnd:', isSeriesEnd);
if (isSeriesEnd) {
// All tasks are done/cancelled, queue is now idle
cleanupOrNotify();
}
});
queue.onError(({ error, isSeriesEnd }) => {
console.error('Task error:', error, 'isSeriesEnd:', isSeriesEnd);
if (isSeriesEnd) {
// Show global error or reset UI
}
});
queue.onComplete(({ result, isSeriesEnd }) => {
console.log('Completed with', result, 'isSeriesEnd:', isSeriesEnd);
if (isSeriesEnd) {
// Final UI update, e.g. loading spinner off
}
});
queue.run(async signal => {
await doSomething(signal);
return 42;
});
// Manual abort of queue.result
queue.abort();
LastWinsAndCancelsPrevious<R>run(task: (signal: AbortSignal) => Promise<R>): Promise<R | undefined> — start a new task, cancels previousresult: Promise<R> | undefined — promise for last task, or undefined if idleabort(): void — manually abort current taskonAborted(cb) — subscribe to any task abortonError(cb) — subscribe to any task erroronComplete(cb) — subscribe to any task completion(args: { result?: R; error?: any; aborted: boolean; signal: AbortSignal; isSeriesEnd: boolean }) => void
isSeriesEnd: boolean — true if this event marks the end of the current run series (queue is now idle), false if another task is queued or running.isSeriesEnd?isSeriesEnd: true — This event marks the end of a "series" (all tasks are done/cancelled, queue is idle)isSeriesEnd: false — This event is intermediate (another task is queued or running)Use this to distinguish between final/finalizing UI actions vs. intermediate (e.g. loading spinners, notifications, analytics, etc).
signal.aborted in your tasks to avoid work after cancellation.result is rejected with that errorrun returns Promise.resolve(undefined).result, even if they finish laterabort() to cancel on navigation or user action.result always reflects only the last completed task, cancelled ones are ignored..result is rejected only with the last task's error. Errors from cancelled tasks are ignored..result and hooks fire only for actually started tasks.test/basic.test.ts for scenariosMIT
FAQs
 [](https://codecov.io/gh/avfirsov/last-wins-and-cancels-pre
We found that last-wins-and-cancels-prevs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.