@@ -83,36 +83,34 @@ foam.CLASS({
8383 var self = this ;
8484 var key = [ sink , skip , limit , order , predicate ] . toString ( ) ;
8585
86- if ( this . cache [ key ] ) {
87- // console.log('************************ TTL CACHED:', key);
88- if ( this . cache [ key ] . clone ) {
89- /// Need to clone to get expressions to re-evaluate
90- return Promise . resolve ( this . cache [ key ] . clone ( ) ) ;
91- } else {
92- return Promise . resolve ( this . cache [ key ] ) ;
93- }
94- }
86+ var entry = this . cache [ key ] ;
9587
96- return new Promise ( function ( resolve , reject ) {
97- self . delegate . select_ ( x , sink , skip , limit , order , predicate ) . then ( s => {
98- // Clone before storing to prevent cache corruption from mutations.
99- // Since objects are passed by reference in JavaScript, storing 's' directly
100- // would cache the same object returned to the caller. Any mutations to the
101- // returned sink (e.g., modifying array contents, changing properties) would
102- // corrupt the cached copy, causing incorrect results on subsequent cache hits.
103- var sinkToCache = s . clone ? s . clone ( ) : s ;
104- self . cache [ key ] = sinkToCache ;
105- // console.log('************************ TTL CACHING:', key);
106- // TODO: check if cache is > maxCacheSize and remove oldest entry if it is
107- self . purgeCache ( ) ;
108- resolve ( s ) ;
109- } ,
110- e => {
111- // console.log('************************ TTL ERROR:', e);
112- self . cache = { } ;
113- reject ( e ) ;
88+ if ( entry ) {
89+ // In-flight promise — concurrent callers share one request
90+ if ( entry instanceof Promise ) return entry . then ( function ( s ) {
91+ return s . clone ? s . clone ( ) : s ;
11492 } ) ;
93+
94+ // Resolved result — clone to prevent cache corruption
95+ if ( entry . clone ) return Promise . resolve ( entry . clone ( ) ) ;
96+ return Promise . resolve ( entry ) ;
97+ }
98+
99+ // Store the promise immediately so concurrent calls deduplicate
100+ var promise = self . delegate . select_ ( x , sink , skip , limit , order , predicate ) . then ( function ( s ) {
101+ // Replace promise with resolved result for future cache hits
102+ var sinkToCache = s . clone ? s . clone ( ) : s ;
103+ self . cache [ key ] = sinkToCache ;
104+ self . purgeCache ( ) ;
105+ return s ;
106+ } , function ( e ) {
107+ delete self . cache [ key ] ;
108+ throw e ;
115109 } ) ;
110+
111+ this . cache [ key ] = promise ;
112+
113+ return promise ;
116114 } ,
117115
118116 /** Removes are sent to the cache and to the source, ensuring both
0 commit comments