1919 */
2020
2121import { ABORT_ERROR , TIMEOUT_ERROR } from './constants' ;
22- import type { InFlightItem } from './types/inflight-manager ' ;
22+ import { addTimeout , removeTimeout } from './timeout-wheel ' ;
2323import { timeNow } from './utils' ;
2424
25+ export type InFlightItem = [
26+ AbortController , // AbortController for the request
27+ boolean , // Whether timeout is enabled for the request
28+ number , // Timestamp when the request was marked in-flight
29+ boolean , // isCancellable - whether the request can be cancelled
30+ Promise < unknown > | null , // Optional in-flight promise for deduplication
31+ ] ;
32+
2533const inFlight : Map < string , InFlightItem > = new Map ( ) ;
2634
2735/**
@@ -39,56 +47,65 @@ export async function markInFlight(
3947 key : string | null ,
4048 url : string ,
4149 timeout : number | undefined ,
42- dedupeTime : number = 0 ,
43- isCancellable : boolean = false ,
44- isTimeoutEnabled : boolean = true ,
50+ dedupeTime : number ,
51+ isCancellable : boolean ,
52+ isTimeoutEnabled : boolean ,
4553) : Promise < AbortController > {
4654 if ( ! key ) {
4755 return new AbortController ( ) ;
4856 }
4957
5058 const item = inFlight . get ( key ) ;
59+ let prevPromise : Promise < unknown > | null = null ;
5160
61+ // Previous request is in-flight, check if we can reuse it
5262 if ( item ) {
53- const previousController = item [ 0 ] ;
63+ const prevController = item [ 0 ] ;
5464 const prevIsCancellable = item [ 3 ] ;
5565
5666 // If the request is already in the queue and within the dedupeTime, reuse the existing controller
57- if ( ! prevIsCancellable && dedupeTime && timeNow ( ) - item [ 2 ] < dedupeTime ) {
58- return previousController ;
67+ if (
68+ ! prevIsCancellable &&
69+ timeNow ( ) - item [ 2 ] < dedupeTime &&
70+ ! prevController . signal . aborted
71+ ) {
72+ return prevController ;
5973 }
6074
6175 // If the request is too old, remove it and proceed to add a new one
6276 // Abort previous request, if applicable, and continue as usual
6377 if ( prevIsCancellable ) {
64- previousController . abort (
78+ prevController . abort (
6579 new DOMException ( 'Aborted due to new request' , ABORT_ERROR ) ,
6680 ) ;
6781 }
6882
69- const timeoutId = item [ 1 ] ;
70-
71- if ( timeoutId !== null ) {
72- clearTimeout ( timeoutId ) ;
73- }
74-
75- inFlight . delete ( key ) ;
83+ removeTimeout ( key ) ;
84+ prevPromise = item [ 4 ] ;
7685 }
7786
7887 const controller = new AbortController ( ) ;
7988
80- const timeoutId = isTimeoutEnabled
81- ? setTimeout ( ( ) => {
82- const error = new DOMException (
83- `${ url } aborted due to timeout` ,
84- TIMEOUT_ERROR ,
89+ inFlight . set ( key , [
90+ controller ,
91+ isTimeoutEnabled ,
92+ timeNow ( ) ,
93+ isCancellable ,
94+ prevPromise ,
95+ ] ) ;
96+
97+ if ( isTimeoutEnabled ) {
98+ addTimeout (
99+ key ,
100+ ( ) => {
101+ abortRequest (
102+ key ,
103+ new DOMException ( url + ' aborted due to timeout' , TIMEOUT_ERROR ) ,
85104 ) ;
86-
87- abortRequest ( key , error ) ;
88- } , timeout )
89- : null ;
90-
91- inFlight . set ( key , [ controller , timeoutId , timeNow ( ) , isCancellable ] ) ;
105+ } ,
106+ timeout as number ,
107+ ) ;
108+ }
92109
93110 return controller ;
94111}
@@ -97,7 +114,8 @@ export async function markInFlight(
97114 * Removes a request from the queue and clears its timeout.
98115 *
99116 * @param key - Unique key for the request.
100- * @param {boolean } error - Error payload so to force the request to abort.
117+ * @param {boolean } error - Optional error to abort the request with. If null, the request is simply removed but no abort sent.
118+ * @returns {Promise<void> } - A promise that resolves when the request is aborted and removed.
101119 */
102120export async function abortRequest (
103121 key : string | null ,
@@ -112,17 +130,13 @@ export async function abortRequest(
112130
113131 if ( item ) {
114132 const controller = item [ 0 ] ;
115- const timeoutId = item [ 1 ] ;
116133
117134 // If the request is not yet aborted, abort it with the provided error
118- if ( error && ! controller . signal . aborted ) {
135+ if ( error ) {
119136 controller . abort ( error ) ;
120137 }
121138
122- if ( timeoutId !== null ) {
123- clearTimeout ( timeoutId ) ;
124- }
125-
139+ removeTimeout ( key ) ;
126140 inFlight . delete ( key ) ;
127141 }
128142}
@@ -175,16 +189,20 @@ export function getInFlightPromise<T = unknown>(
175189 return null ;
176190 }
177191
178- const item = inFlight . get ( key ) ;
192+ const prevReq = inFlight . get ( key ) ;
179193
180194 if (
181- item &&
182- item [ 4 ] &&
183- timeNow ( ) - item [ 2 ] < dedupeTime &&
195+ prevReq &&
196+ // If the request is in-flight and has a promise
197+ prevReq [ 4 ] &&
198+ // If the request is cancellable, we will not reuse it
199+ ! prevReq [ 3 ] &&
200+ // If the request is within the dedupeTime
201+ timeNow ( ) - prevReq [ 2 ] < dedupeTime &&
184202 // If one request is cancelled, ALL deduped requests get cancelled
185- ! item [ 0 ] . signal . aborted
203+ ! prevReq [ 0 ] . signal . aborted
186204 ) {
187- return item [ 4 ] as Promise < T > ;
205+ return prevReq [ 4 ] as Promise < T > ;
188206 }
189207
190208 return null ;
0 commit comments