javascript
37 lines · 8 steps
Cancelling stale requests in a search client
A closure tracks the in-flight request so each new search aborts the previous one before firing.
Explained by
highlit
1export function createSearchClient(baseUrl) {
2 let inFlight = null;
3
4 async function search(query, { signal } = {}) {
5 if (inFlight) {
6 inFlight.abort();
7 }
8
9 const controller = new AbortController();
10 inFlight = controller;
11
12 if (signal) {
13 signal.addEventListener('abort', () => controller.abort(), { once: true });
14 }
15
16 const url = `${baseUrl}/search?q=${encodeURIComponent(query)}`;
17
18 try {
19 const response = await fetch(url, { signal: controller.signal });
20 if (!response.ok) {
21 throw new Error(`Search failed: ${response.status}`);
22 }
23 return await response.json();
24 } catch (err) {
25 if (err.name === 'AbortError') {
26 return null;
27 }
28 throw err;
29 } finally {
30 if (inFlight === controller) {
31 inFlight = null;
32 }
33 }
34 }
35
36 return { search };
37}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Keeping a reference to the current request lets you cancel it the moment a newer one starts.
- 2Chaining an external signal to an internal controller lets callers cancel while the client still manages its own state.
- 3Treating AbortError as a non-error keeps intentional cancellations from crashing the caller.
Related explainers
javascript
import { NextResponse } from 'next/server'; import { Redis } from '@upstash/redis'; const redis = Redis.fromEnv();
Sliding-window rate limiting in a Next.js route
rate-limiting
redis
sorted-set
Advanced
8 steps
javascript
const MAX_FILE_SIZE = 5 * 1024 * 1024; const ALLOWED_TYPES = { 'image/jpeg': ['jpg', 'jpeg'],
Validating file uploads by content, not just claims
input-validation
security
magic-bytes
Intermediate
7 steps
javascript
function zip(keys, values) { if (keys.length !== values.length) { throw new RangeError('zip expects arrays of equal length'); }
Three ways to zip arrays in JavaScript
arrays
higher-order-functions
pairing
Intermediate
6 steps
java
public class TimedFetchService { private final ExecutorService executor = Executors.newFixedThreadPool(8); private final HttpClient httpClient = HttpClient.newHttpClient();
Enforcing HTTP timeouts with a Future
concurrency
timeouts
thread-pool
Intermediate
8 steps
go
package middleware import ( "net/http"
Role-based access control middleware in Gin
middleware
authorization
closures
Intermediate
7 steps
javascript
import { useCallback, useEffect, useState } from 'react'; export function useLocalStorage(key, initialValue) { const readValue = useCallback(() => {
How a useLocalStorage hook syncs state in React
custom hooks
localstorage
state persistence
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/cancelling-stale-requests-in-a-search-client-explained-javascript-980c/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.