@@ -25,8 +25,10 @@ function generateCacheKey(request: Request): string {
25
25
}
26
26
27
27
export function createDedupeFetch ( originalFetch : typeof fetch ) {
28
- // eslint-disable-next-line @typescript-eslint/no-unused-vars -- url is the cache key
29
- const getCacheEntries = React . cache ( ( url : string ) : Array < any > => [ ] )
28
+ const getCacheEntries = React . cache (
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- url is the cache key
30
+ ( url : string ) : Array < [ key : string , promise : Promise < Response > ] > => [ ]
31
+ )
30
32
31
33
return function dedupeFetch (
32
34
resource : URL | RequestInfo ,
@@ -42,6 +44,7 @@ export function createDedupeFetch(originalFetch: typeof fetch) {
42
44
// Request constructor.
43
45
return originalFetch ( resource , options )
44
46
}
47
+
45
48
// Normalize the Request
46
49
let url : string
47
50
let cacheKey : string
@@ -73,30 +76,36 @@ export function createDedupeFetch(originalFetch: typeof fetch) {
73
76
url = request . url
74
77
}
75
78
79
+ // Get the cache entries for the given URL.
76
80
const cacheEntries = getCacheEntries ( url )
77
- let match
78
- if ( cacheEntries . length === 0 ) {
79
- // We pass the original arguments here in case normalizing the Request
80
- // doesn't include all the options in this environment.
81
- match = originalFetch ( resource , options )
82
- cacheEntries . push ( cacheKey , match )
83
- } else {
84
- // We use an array as the inner data structure since it's lighter and
85
- // we typically only expect to see one or two entries here.
86
- for ( let i = 0 , l = cacheEntries . length ; i < l ; i += 2 ) {
87
- const key = cacheEntries [ i ]
88
- const value = cacheEntries [ i + 1 ]
89
- if ( key === cacheKey ) {
90
- match = value
91
- // I would've preferred a labelled break but lint says no.
92
- return match . then ( ( response : Response ) => response . clone ( ) )
93
- }
81
+
82
+ // Check if there is a cached entry for the given cache key. If there is, we
83
+ // return the cached response (cloned). This will keep the cached promise to
84
+ // remain unused and can be cloned on future requests.
85
+ for ( const [ key , promise ] of cacheEntries ) {
86
+ if ( key !== cacheKey ) {
87
+ continue
94
88
}
95
- match = originalFetch ( resource , options )
96
- cacheEntries . push ( cacheKey , match )
89
+
90
+ return promise . then ( ( response : Response ) => response . clone ( ) )
97
91
}
98
- // We clone the response so that each time you call this you get a new read
99
- // of the body so that it can be read multiple times.
100
- return match . then ( ( response ) => response . clone ( ) )
92
+
93
+ // We pass the original arguments here in case normalizing the Request
94
+ // doesn't include all the options in this environment.
95
+ const original = originalFetch ( resource , options )
96
+
97
+ // We then clone the original response. We store this in the cache so that
98
+ // any future requests will be using this cloned response.
99
+ const cloned = original . then ( ( response ) => response . clone ( ) )
100
+
101
+ // Attach an empty catch here so we don't get a "unhandled promise
102
+ // rejection" warning
103
+ cloned . catch ( ( ) => { } )
104
+
105
+ cacheEntries . push ( [ cacheKey , cloned ] )
106
+
107
+ // Return the promise so that the caller can await it. We pass back the
108
+ // original promise.
109
+ return original
101
110
}
102
111
}
0 commit comments