@@ -2,10 +2,12 @@ import { ActionContext } from 'vuex';
2
2
import { NuxtHTTPInstance } from '@nuxt/http' ;
3
3
import { Context } from '@nuxt/types' ;
4
4
5
- export interface OengusStateCacheable {
6
- _expires : number ;
5
+ export interface OengusStateCacheable < V > {
6
+ _cachedAt : number ;
7
+ _fetching : boolean ;
8
+ _promise : Promise < V & OengusStateCacheable < V > > ;
7
9
}
8
- export type OengusStateValue < V > = V & OengusStateCacheable | OengusStateCacheable | undefined ;
10
+ export type OengusStateValue < V > = V & OengusStateCacheable < V > | OengusStateCacheable < V > | undefined ;
9
11
export interface OengusStateValuesById < V > {
10
12
[ id : string ] : OengusStateValue < V > ;
11
13
}
@@ -39,7 +41,7 @@ export interface GetterArgs<U, V = U> {
39
41
* Used to alter the raw API response to whatever is stored in the state
40
42
* This can be strip, change, or add new values or even change into a full class
41
43
*/
42
- transform ?( value : U , id ?: number | string ) : V & OengusStateCacheable ;
44
+ transform ?( value : U , id ?: number | string ) : V ;
43
45
/**
44
46
* How long should responses be cached for
45
47
* Can be ignored with forceFetch
@@ -84,44 +86,50 @@ export class OengusAPI<T extends OengusState> {
84
86
}
85
87
86
88
// Cache check
87
- let response : OengusStateValue < U | V > = id ? state [ key ] [ id ] : state [ key ] ;
88
- const cachedTime = response ?. _expires ?? 0 ;
89
- if ( cachedTime + cacheDuration > Date . now ( ) && ! forceFetch ) {
90
- return response as V ;
89
+ const cachedResponse : OengusStateValue < V > = id ? state [ key ] [ id ] : state [ key ] ;
90
+ const cachedTime = cachedResponse ?. _cachedAt ?? 0 ;
91
+ const fetching = cachedResponse ?. _fetching ?? false ;
92
+ if ( fetching || ( ! forceFetch && cachedTime + cacheDuration > Date . now ( ) ) ) {
93
+ return cachedResponse as V ;
91
94
}
92
95
96
+ // Setup the fetching promise
97
+ let fetchingResolve : ( value : OengusStateValue < V > ) => void ;
98
+ let fetchingReject : ( reason : { reason ?: any , oldValue : OengusStateValue < V > } ) => void ;
99
+ const fetchingPromise = new Promise < OengusStateValue < V > > ( ( resolve , reject ) => {
100
+ fetchingResolve = resolve ;
101
+ fetchingReject = reject ;
102
+ } ) ;
103
+
104
+ // Mark the entry as updating by changing _fetching property
93
105
if ( cachedTime ) {
94
- // Mark the entry as updating by changing the cache expired marker
95
106
// This allows existing stuff to continue to use the cached value,
96
- commit ( mutation , { id, value : { ...response , _expires : Date . now ( ) } } ) ;
107
+ commit ( mutation , { id, value : { ...cachedResponse , _fetching : true , _promise : fetchingPromise } } ) ;
97
108
} else {
98
- // Mark the entry as "being fetched" by giving it a promise
99
- // Anything that wants to read the value is welcome to await it or use Vuex's observers
100
- commit ( mutation , { id, value : { _expires : Date . now ( ) } } ) ;
109
+ commit ( mutation , { id, value : { _cachedAt : Date . now ( ) , _fetching : true , _promise : fetchingPromise } } ) ;
101
110
}
102
111
103
112
// Fetch and store into cache
104
113
const route = `${ this . basePath } ${ id ? `/${ id } ` : '' } ${ path ? `/${ path } ` : '' } ` ;
105
- let apiResponse : U & OengusStateCacheable ;
114
+ let apiResponse : U ;
106
115
try {
107
116
apiResponse = await OengusAPI . http . $get ( route ) ;
108
- } catch {
109
- // This isn't intrinsically bad, just catch the error, mark as not fetching, and return nothing
110
- if ( cachedTime ) {
111
- // Reset the old cache timer, maybe the API is just down (should we worry about loops?)
112
- commit ( mutation , { id, value : response } ) ;
113
- } else {
114
- commit ( mutation , { id, value : undefined } ) ;
115
- }
117
+ } catch ( error ) {
118
+ // This isn't intrinsically bad, just catch the error, mark as not fetching, and return the old value
119
+ // Put the old value back in full, maybe the API is just down (should we worry about loops?)
120
+ fetchingReject ! ( { reason : error , oldValue : cachedResponse } ) ;
121
+ commit ( mutation , { id, value : cachedResponse } ) ;
116
122
return ;
117
123
}
118
- response = apiResponse ;
119
124
120
- if ( transform ) {
121
- response = transform ( response as U , id ) ;
122
- }
123
- response . _expires = Date . now ( ) ;
124
- commit ( mutation , { id, value : response as OengusStateValue < V > } ) ;
125
+ const response = {
126
+ ...( transform ? transform ( apiResponse as U , id ) : apiResponse ) ,
127
+ _fetching : false ,
128
+ _cachedAt : Date . now ( ) ,
129
+ _promise : fetchingPromise ,
130
+ } as OengusStateValue < V > ;
131
+ fetchingResolve ! ( response ) ;
132
+ commit ( mutation , { id, value : response } ) ;
125
133
return response as V ;
126
134
} ;
127
135
}
0 commit comments