@@ -18,7 +18,8 @@ export class Service {
18
18
DEBOUNCE_TIME_MS = 200 ;
19
19
_broadcast = true ;
20
20
/** @type {undefined|((old: Data, next: Data, trigger: InvocationSource) => Data) } */
21
- accept ;
21
+ updater ;
22
+ nonOverlapping = new NonOverlapping ( this ) ;
22
23
/**
23
24
* @param {object } props
24
25
* @param {(arg?: any) => Promise<Data> } [props.initial]
@@ -41,7 +42,7 @@ export class Service {
41
42
* @param {(old: Data, next: Data, trigger: InvocationSource) => Data } fn
42
43
*/
43
44
withUpdater ( fn ) {
44
- this . accept = fn ;
45
+ this . updater = fn ;
45
46
return this ;
46
47
}
47
48
@@ -52,7 +53,7 @@ export class Service {
52
53
async fetchInitial ( params ) {
53
54
if ( ! this . impl . initial ) throw new Error ( 'unreachable' ) ;
54
55
const initial = await this . impl . initial ( params ) ;
55
- this . _accept ( initial , 'initial' ) ;
56
+ this . accept ( initial , 'initial' ) ;
56
57
return /** @type {Data } */ ( this . data ) ;
57
58
}
58
59
@@ -63,7 +64,7 @@ export class Service {
63
64
async triggerFetch ( params ) {
64
65
if ( ! this . impl . initial ) throw new Error ( 'unreachable' ) ;
65
66
const next = await this . impl . initial ( params ) ;
66
- this . _accept ( next , 'trigger-fetch' ) ;
67
+ this . accept ( next , 'trigger-fetch' ) ;
67
68
return /** @type {Data } */ ( this . data ) ;
68
69
}
69
70
@@ -104,7 +105,7 @@ export class Service {
104
105
_setupSubscription ( ) {
105
106
if ( this . sub ) return ;
106
107
this . sub = this . impl . subscribe ?. ( ( data ) => {
107
- this . _accept ( data , 'subscription' ) ;
108
+ this . accept ( data , 'subscription' ) ;
108
109
} ) ;
109
110
}
110
111
@@ -117,7 +118,7 @@ export class Service {
117
118
}
118
119
119
120
flush ( ) {
120
- if ( this . data ) this . _accept ( this . data , 'manual' ) ;
121
+ if ( this . data ) this . accept ( this . data , 'manual' ) ;
121
122
}
122
123
123
124
/**
@@ -132,19 +133,18 @@ export class Service {
132
133
if ( this . data === null ) return ;
133
134
const next = updaterFn ( this . data ) ;
134
135
if ( next ) {
135
- this . _accept ( next , 'manual' ) ;
136
+ this . accept ( next , 'manual' ) ;
136
137
} else {
137
138
console . warn ( 'could not update' ) ;
138
139
}
139
140
}
140
141
/**
141
142
* @param {Data } data
142
143
* @param {InvocationSource } source
143
- * @private
144
144
*/
145
- _accept ( data , source ) {
146
- if ( this . accept && source !== 'initial' ) {
147
- this . data = /** @type {NonNullable<Data> } */ ( this . accept ( /** @type {NonNullable<Data> } */ ( this . data ) , data , source ) ) ;
145
+ accept ( data , source ) {
146
+ if ( this . updater && source !== 'initial' ) {
147
+ this . data = /** @type {NonNullable<Data> } */ ( this . updater ( /** @type {NonNullable<Data> } */ ( this . data ) , data , source ) ) ;
148
148
} else {
149
149
this . data = /** @type {NonNullable<Data> } */ ( data ) ;
150
150
}
@@ -199,3 +199,32 @@ export class Service {
199
199
this . impl . persist ( this . data ) ;
200
200
}
201
201
}
202
+
203
+ class NonOverlapping {
204
+ /** @type {null|{promise: any} } */
205
+ _ongoing = null ;
206
+
207
+ /**
208
+ * @param {Service } service
209
+ */
210
+ constructor ( service ) {
211
+ this . service = service ;
212
+ }
213
+
214
+ /**
215
+ * @param {any } [params]
216
+ */
217
+ triggerFetch ( params ) {
218
+ if ( ! this . service . impl . initial ) throw new Error ( 'unreachable' ) ;
219
+ if ( this . _ongoing ?. promise ) {
220
+ this . _ongoing . promise . cancelled = true ;
221
+ }
222
+ // eslint-disable-next-line promise/prefer-await-to-then
223
+ const promise = this . service . impl . initial ( params ) . then ( ( data ) => {
224
+ if ( /** @type {any } */ ( promise ) . cancelled ) return ;
225
+ this . service . accept ( data , 'trigger-fetch' ) ;
226
+ this . _ongoing = null ;
227
+ } ) ;
228
+ this . _ongoing = { promise } ;
229
+ }
230
+ }
0 commit comments