typescript 43 lines · 7 steps

Cancelling stale searches with AbortController

A search client that aborts the in-flight request whenever a newer query arrives, so only the latest result wins.

Explained by highlit
1type SearchResult = {
2 id: string;
3 title: string;
4};
5 
6export class SearchClient {
7 private controller: AbortController | null = null;
8 
9 async search(query: string): Promise<SearchResult[]> {
10 this.controller?.abort();
11 
12 const controller = new AbortController();
13 this.controller = controller;
14 
15 try {
16 const response = await fetch(
17 `/api/search?q=${encodeURIComponent(query)}`,
18 { signal: controller.signal },
19 );
20 
21 if (!response.ok) {
22 throw new Error(`Search failed with status ${response.status}`);
23 }
24 
25 const results = (await response.json()) as SearchResult[];
26 return results;
27 } catch (error) {
28 if (error instanceof DOMException && error.name === "AbortError") {
29 return [];
30 }
31 throw error;
32 } finally {
33 if (this.controller === controller) {
34 this.controller = null;
35 }
36 }
37 }
38 
39 cancel(): void {
40 this.controller?.abort();
41 this.controller = null;
42 }
43}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Storing the active AbortController as state lets a newer call cancel the previous one before it resolves.
  2. 2Distinguishing AbortError from real failures keeps intentional cancellations from surfacing as errors.
  3. 3A finally block that only clears state when the controller still matches avoids wiping out a newer request's controller.

Related explainers

typescript
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';
import localeDe from '@angular/common/locales/de';

Locale-aware bootstrapping in Angular

i18n localization dependency-injection
Intermediate 8 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
 
app = FastAPI()
 

Building a WebSocket chat with FastAPI

websockets broadcast connection-management
Intermediate 9 steps
typescript
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
 

Validating env config at boot in NestJS

configuration schema-validation environment-variables
Intermediate 8 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { InjectRepository } from '@nestjs/typeorm';

A cache-aside country lookup in NestJS

cache-aside dependency-injection batch-lookup
Intermediate 8 steps
rust
use axum::{
    extract::{Path, State},
    response::sse::{Event, KeepAlive, Sse},
};

Streaming import progress with SSE in Axum

server-sent-events streams watch-channel
Advanced 7 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core';
 
interface Preferences {
  theme: 'light' | 'dark';

A signal-based preferences store in Angular

signals state-management persistence
Intermediate 7 steps

Share this explainer

Here's the card — post it anywhere.

Cancelling stale searches with AbortController — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code