Skip to content

services track

Eugene Lazutkin edited this page Jul 4, 2026 · 6 revisions

Track

io.track deduplicates in-flight requests at the run level: concurrent identical GETs share a single decoded envelope instead of each hitting the network and reparsing the body.

import io from 'double-meh';

const [a, b] = await Promise.all([
  io.get('https://example.com/dup'),
  io.get('https://example.com/dup')
]);
// one network call; a and b are the same decoded result

Decoded-envelope dedup

The first caller of a key marks it in-flight and runs the request; later callers awaiting the same key receive the same resolved envelope — decoded once, not cloned and reparsed. Dedup is keyed by method + URL + representation (the effective Accept — see keys & URLs), so concurrent GETs asking for different representations each get their own request rather than sharing one body.

const [a, b] = await Promise.all([
  io.full.get('https://example.com/shared'),
  io.full.get('https://example.com/shared')
]);
a === b; // true — one envelope object, decoded at the run level

Tracking is GET-only, hard: track: true on a non-GET does nothing. Within GETs, track: false opts a request out, and io.track.theDefault — a boolean or a predicate (options) => boolean, default options => !options.transport — decides for requests that don't say. So GETs on a custom transport are not tracked unless they pass track: true.

Cancellation is refcounted

The shared request runs on the tracker's own signal, not any caller's. Aborting your signal detaches only you — your promise rejects with your AbortError (or TimedOut), while other waiters keep the request alive and get the response. The wire request aborts only when the last waiter leaves; a caller without a signal pins it to completion.

const mine = new AbortController();
const a = io.get('/report', null, {signal: mine.signal});
const b = io.get('/report'); // deduped onto the same request
mine.abort(); // a rejects; b still resolves with the response

Skips streaming and custom decodes

stream: true requests are never deduped — each streaming caller gets its own response and body. A request with a decode override is not deduped either: the shared envelope is decoded once, with the leader's settings, so a custom decode must stay private. (The cache is unaffected — it stores bytes, and every reader decodes its own copy.)

await Promise.all([
  io.full.get('https://example.com/s', null, {stream: true}),
  io.full.get('https://example.com/s', null, {stream: true})
]); // two independent responses

track: 'wait'

track: 'wait' registers interest in a key without firing a request. The returned promise stays pending until a later real request (or io.adopt) for the same key lands, then resolves every waiter with that result. track: 'wait' on a non-trackable request (a non-GET, or a stream) throws a TypeError.

const waiter = io.get({url: 'https://example.com/w', track: 'wait'});
// nothing has been sent yet
const real = io.get('https://example.com/w'); // the real request fires
const [w, r] = await Promise.all([waiter, real]); // both resolve to the landed data

fly, flyByKey, isFlying

These expose the in-flight registry directly:

  • io.track.fly(target) — get (creating if needed) the deferred for a target.
  • io.track.flyByKey(key) — same, addressed by a raw key from io.makeKey.
  • io.track.isFlying(target) — the deferred if the key is currently in flight, else undefined.
io.track.fly('https://example.com/soon'); // register interest by target
const pending = io.track.isFlying('https://example.com/soon');
if (pending) await pending.promise;

Relationship to io.adopt

io.adopt(target, source) fulfills a key from an externally issued response (a Promise<Response> or Response) instead of the network — it marks the key flying so a later real request does not also fire, resolves all waiters, and (when the cache is active and the request is cacheable) saves the GET. This is the basis of the code-forward handoff: a page can pre-register in-flight keys via fly and later feed their responses via adopt, so client hydration reuses server-issued data.

io.adopt('https://example.com/me', json({from: 'prelude'}));
const data = await io.get('https://example.com/me'); // no network — uses the adopted response

Attaching

The main entry attaches tracking for you. io.track.attach() / io.track.detach() toggle io.track.active and return io.

io.track.detach(); // disable dedup
io.track.attach(); // re-enable

See also

Clone this wiki locally