@@ -182,6 +182,64 @@ const useAsyncEffect = (generator, options) => {
182
182
183
183
const argsToPromiseMap = new Map ( ) ;
184
184
185
+ const asyncEffectFactory = ( options ) => {
186
+ if ( options != null ) {
187
+ if ( typeof options !== 'object' ) {
188
+ throw TypeError ( 'options must be an object' ) ;
189
+ }
190
+
191
+ if ( options . threads === undefined ) {
192
+ options . threads = options . cancelPrevious || options . states ? 1 : 0 ;
193
+ } else {
194
+ if ( ! Number . isFinite ( options . threads ) || options . threads < 0 ) {
195
+ throw Error ( 'threads must be a positive number' ) ;
196
+ }
197
+
198
+ if ( options . states && options . threads !== 1 ) {
199
+ throw Error ( `Can not use states in not single threaded async function` ) ;
200
+ }
201
+ }
202
+
203
+ if ( options . queueSize !== undefined && ( ! Number . isFinite ( options . queueSize ) || options . queueSize < - 1 ) ) {
204
+ throw Error ( 'queueSize must be a finite number >=-1' ) ;
205
+ }
206
+ }
207
+
208
+ const promises = [ ] ;
209
+
210
+ return {
211
+ promises,
212
+ queue : [ ] ,
213
+ pending : 0 ,
214
+ args : null ,
215
+ cancel : ( reason ) => {
216
+ const _reason = isEvent ( reason ) ? undefined : reason ;
217
+ promises . forEach ( promise => promise . cancel ( _reason ) ) ;
218
+ promises . length = 0 ;
219
+ } ,
220
+ pause : ( data ) => promises . forEach ( promise => promise . pause ( data ) ) ,
221
+ resume : ( data ) => promises . forEach ( promise => promise . resume ( data ) ) ,
222
+ initialState : {
223
+ done : false ,
224
+ result : undefined ,
225
+ error : undefined ,
226
+ canceled : false ,
227
+ pending : false
228
+ } ,
229
+ options : {
230
+ deps : [ ] ,
231
+ combine : false ,
232
+ cancelPrevious : false ,
233
+ threads : 0 ,
234
+ queueSize : - 1 ,
235
+ scopeArg : false ,
236
+ states : false ,
237
+ catchErrors : false ,
238
+ ...( Array . isArray ( options ) ? { deps : options } : options )
239
+ }
240
+ } ;
241
+ }
242
+
185
243
/**
186
244
* @typedef {Function } UseAsyncCallbackDecoratedFn
187
245
* @param {CPromise } [scope]
@@ -214,65 +272,32 @@ const argsToPromiseMap = new Map();
214
272
* @returns {UseAsyncCallbackDecoratedFn }
215
273
*/
216
274
const useAsyncCallback = ( generator , options ) => {
217
- if ( options != null && typeof options !== 'object' ) {
218
- throw TypeError ( 'options must be an object or array' ) ;
219
- }
220
-
221
- const initialState = {
222
- done : false ,
223
- result : undefined ,
224
- error : undefined ,
225
- canceled : false ,
226
- pending : false
227
- } ;
228
-
229
- const { current} = useRef ( {
230
- promises : [ ] ,
231
- queue : [ ] ,
232
- pending : 0 ,
233
- args : null ,
234
- callback : null
235
- } ) ;
275
+ const current = useFactory ( asyncEffectFactory , [ options ] ) ;
236
276
237
277
let {
238
- deps = [ ] ,
239
- combine = false ,
240
- cancelPrevious = false ,
241
- threads,
242
- queueSize = - 1 ,
243
- scopeArg = false ,
244
- states = false ,
245
- catchErrors = false
246
- } = options && Array . isArray ( options ) ? { deps : options } : options || { } ;
247
-
248
- if ( threads === undefined ) {
249
- threads = cancelPrevious || states ? 1 : 0 ;
250
- } else {
251
- if ( ! Number . isFinite ( threads ) || threads < 0 ) {
252
- throw Error ( 'threads must be a positive number' ) ;
278
+ initialState,
279
+ options : {
280
+ deps,
281
+ combine,
282
+ cancelPrevious,
283
+ threads,
284
+ queueSize,
285
+ scopeArg,
286
+ states,
287
+ catchErrors
253
288
}
254
- }
255
-
256
- if ( queueSize !== - 1 && ( ! Number . isFinite ( queueSize ) || queueSize < - 1 ) ) {
257
- throw Error ( 'queueSize must be a finite number >=-1' ) ;
258
- }
289
+ } = current ;
259
290
260
291
const [ state , setState ] = states ? useAsyncDeepState ( initialState , { watch : false } ) : [ ] ;
261
292
262
- const singleThreaded = threads === 1 ;
263
-
264
293
const callback = useMemo ( ( ) => {
265
- const { promises, queue} = current ;
266
-
267
- const cancel = ( reason ) => {
268
- const _reason = isEvent ( reason ) ? undefined : reason ;
269
- promises . forEach ( promise => promise . cancel ( _reason ) ) ;
270
- promises . length = 0 ;
271
- } ;
272
-
273
- const pause = ( data ) => promises . forEach ( promise => promise . pause ( data ) ) ;
274
-
275
- const resume = ( data ) => promises . forEach ( promise => promise . resume ( data ) ) ;
294
+ const {
295
+ promises,
296
+ queue,
297
+ cancel,
298
+ pause,
299
+ resume
300
+ } = current ;
276
301
277
302
const fn = ( ...args ) => {
278
303
let n ;
@@ -304,7 +329,7 @@ const useAsyncCallback = (generator, options) => {
304
329
305
330
started = true ;
306
331
307
- if ( states && singleThreaded ) {
332
+ if ( states ) {
308
333
setState ( {
309
334
...initialState ,
310
335
pending : true ,
@@ -335,7 +360,7 @@ const useAsyncCallback = (generator, options) => {
335
360
return ;
336
361
}
337
362
338
- states && singleThreaded && setState ( {
363
+ states && setState ( {
339
364
pending : false ,
340
365
done : true ,
341
366
error : isRejected ? value : undefined ,
@@ -346,7 +371,7 @@ const useAsyncCallback = (generator, options) => {
346
371
return isRejected ? undefined : value ;
347
372
} ) . weight ( 0 ) . aggregate ( ) ;
348
373
349
- if ( states && singleThreaded ) {
374
+ if ( states ) {
350
375
promise . onPause ( ( ) => setState ( {
351
376
paused : true
352
377
} ) )
@@ -379,59 +404,72 @@ const useAsyncCallback = (generator, options) => {
379
404
return promise ;
380
405
}
381
406
407
+ if ( states ) {
408
+ const makeDescriptor = ( name ) => ( {
409
+ get ( ) {
410
+ return state [ name ] ;
411
+ }
412
+ } )
413
+
414
+ Object . defineProperties ( fn , {
415
+ done : makeDescriptor ( 'done' ) ,
416
+ pending : makeDescriptor ( 'pending' ) ,
417
+ result : makeDescriptor ( 'result' ) ,
418
+ error : makeDescriptor ( 'error' ) ,
419
+ canceled : makeDescriptor ( 'canceled' ) ,
420
+ paused : makeDescriptor ( 'paused' ) ,
421
+ [ Symbol . iterator ] : {
422
+ value : function * ( ) {
423
+ yield * [
424
+ fn ,
425
+ cancel ,
426
+ state . pending ,
427
+ state . done ,
428
+ state . result ,
429
+ state . error ,
430
+ state . canceled ,
431
+ state . paused
432
+ ] ;
433
+ }
434
+ }
435
+ } )
436
+ } else {
437
+ fn [ Symbol . iterator ] = function * ( ) {
438
+ yield * [ fn , cancel ] ;
439
+ }
440
+ }
441
+
382
442
fn . cancel = cancel ;
383
443
384
444
fn . pause = pause ;
385
445
386
446
fn . resume = resume ;
387
447
388
- current . callback = fn ;
389
-
390
- Object . defineProperty ( fn , 'current' , {
391
- get ( ) {
392
- return current . callback ;
393
- } ,
394
- configurable : true
395
- } ) ;
396
-
397
448
return fn ;
398
449
} , deps ) ;
399
450
400
451
useEffect ( ( ) => {
401
452
return ( ) => callback . cancel ( E_REASON_UNMOUNTED ) ;
402
- } , deps ) ;
403
-
404
- if ( states ) {
405
- if ( ! singleThreaded ) {
406
- throw Error ( `Can not use states in not single threaded async function` ) ;
407
- }
408
- callback . done = state . done ;
409
- callback . pending = state . pending ;
410
- callback . result = state . result ;
411
- callback . error = state . error ;
412
- callback . canceled = state . canceled ;
413
- callback . paused = state . paused ;
414
-
415
- callback [ Symbol . iterator ] = function * ( ) {
416
- yield * [
417
- callback ,
418
- callback . cancel ,
419
- state . pending ,
420
- state . done ,
421
- state . result ,
422
- state . error ,
423
- state . canceled ,
424
- state . paused
425
- ] ;
426
- }
427
- } else {
428
- callback [ Symbol . iterator ] = function * ( ) {
429
- yield * [ callback , callback . cancel ] ;
430
- }
431
- }
453
+ } , [ ] ) ;
432
454
433
455
return callback ;
434
456
}
457
+
458
+ const initialized = new WeakSet ( ) ;
459
+
460
+ const useFactory = ( factory , args ) => {
461
+ const { current} = useRef ( { } ) ;
462
+
463
+ if ( initialized . has ( current ) ) return current ;
464
+
465
+ initialized . add ( current ) ;
466
+
467
+ Object . assign ( current , factory . apply ( null , args ) ) ;
468
+
469
+ return current ;
470
+ }
471
+
472
+
435
473
/**
436
474
* useAsyncDeepState hook whose setter returns a promise
437
475
* @param {* } [initialValue]
0 commit comments