Skip to content

Commit b62ea48

Browse files
Refactored useAsyncEffect hook;
Refactored so that an E_REASON_RESTART error does not lead to the state change. Optimized internal state change;
1 parent ea0bc09 commit b62ea48

File tree

1 file changed

+90
-79
lines changed

1 file changed

+90
-79
lines changed

lib/use-async-effect.js

+90-79
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const {useEffect, useCallback, useRef, useState} = require("react");
1+
const {useEffect, useMemo, useRef, useState} = require("react");
22
const {CPromise, CanceledError, E_REASON_UNMOUNTED} = require("c-promise2");
33
const isEqualObjects = require('is-equal-objects/dist/is-equal-objects.cjs');
44

@@ -166,11 +166,20 @@ const useAsyncCallback = (generator, options) => {
166166
throw TypeError('options must be an object or array');
167167
}
168168

169+
const initialState= {
170+
done: false,
171+
result: undefined,
172+
error: undefined,
173+
canceled: false,
174+
pending: false
175+
};
176+
169177
const {current} = useRef({
170178
promises: [],
171179
queue: [],
172180
pending: 0,
173-
args: null
181+
args: null,
182+
state: initialState
174183
});
175184

176185
let {
@@ -200,115 +209,123 @@ const useAsyncCallback = (generator, options) => {
200209
throw Error('queueSize must be a finite number >=-1');
201210
}
202211

203-
const [state, setState]= states? useState({
204-
done: false,
205-
result: undefined,
206-
error: undefined,
207-
canceled: false,
208-
pending: false
209-
}) : [];
212+
const [state, setState]= states? useState(initialState) : [];
213+
214+
const setDeepState = (newState) => {
215+
if (isEqualObjects(current.state, newState)) return;
216+
setState(newState);
217+
current.state = newState;
218+
}
210219

211220
const singleThreaded= threads === 1;
212221

213-
const callback = useCallback((...args) => {
214-
const {promises, queue} = current;
215-
let n;
216-
217-
if (combine && (n = promises.length)) {
218-
let promise;
219-
while (n-- > 0) {
220-
promise = promises[n];
221-
if (argsToPromiseMap.has(promise) && isEqualObjects(argsToPromiseMap.get(promise), args)) {
222-
if (cancelPrevious) {
223-
promise.cancel(E_REASON_RESTART);
224-
break;
222+
const callback = useMemo(()=>{
223+
const cancel= (reason) => {
224+
const _reason = isEvent(reason) ? undefined : reason;
225+
current.promises.forEach(promise => promise.cancel(_reason));
226+
current.promises.length = 0;
227+
};
228+
229+
const fn= (...args) => {
230+
const {promises, queue} = current;
231+
let n;
232+
233+
if (combine && (n = promises.length)) {
234+
let promise;
235+
while (n-- > 0) {
236+
promise = promises[n];
237+
if (argsToPromiseMap.has(promise) && isEqualObjects(argsToPromiseMap.get(promise), args)) {
238+
if (cancelPrevious) {
239+
promise.cancel(E_REASON_RESTART);
240+
break;
241+
}
242+
return CPromise.resolve(promise);
225243
}
226-
return CPromise.resolve(promise);
227244
}
228245
}
229-
}
230246

231-
const resolveGenerator = () => CPromise.run(generator, {args, resolveSignatures: true, scopeArg});
247+
const resolveGenerator = () => CPromise.run(generator, {args, resolveSignatures: true, scopeArg});
232248

233-
cancelPrevious && !combine && cancel(E_REASON_RESTART);
249+
cancelPrevious && !combine && cancel(E_REASON_RESTART);
234250

235-
if (threads || queueSize !== -1) {
236-
let started;
251+
if (threads || queueSize !== -1) {
252+
let started;
237253

238-
let promise = new CPromise(resolve => {
239-
const start= ()=> {
240-
current.pending++;
254+
let promise = new CPromise(resolve => {
255+
const start= ()=> {
256+
current.pending++;
241257

242-
started= true;
258+
started= true;
243259

244-
if (states && singleThreaded) {
245-
setState({
246-
done: false,
247-
pending: true,
248-
canceled: false
249-
})
260+
if (states && singleThreaded) {
261+
setDeepState({
262+
...initialState,
263+
pending: true,
264+
})
265+
}
266+
resolve();
250267
}
251-
resolve();
252-
}
253268

254-
if (current.pending === threads) {
255-
if (queueSize !== -1 && queue.length === queueSize) {
256-
throw CanceledError.from(E_REASON_QUEUE_OVERFLOW);
257-
}
269+
if (current.pending === threads) {
270+
if (queueSize !== -1 && queue.length === queueSize) {
271+
throw CanceledError.from(E_REASON_QUEUE_OVERFLOW);
272+
}
258273

259-
return queue.push(start);
260-
}
274+
return queue.push(start);
275+
}
261276

262-
start();
263-
}).weight(0)
264-
.then(resolveGenerator)
265-
[catchErrors? 'done': 'finally']((value, isRejected) => {
277+
start();
278+
}).weight(0)
279+
.then(resolveGenerator)
280+
[catchErrors? 'done': 'finally']((value, isRejected) => {
266281
started && current.pending--;
267282
removeElement(promises, promise);
268283
combine && argsToPromiseMap.delete(promise);
269284
queue.length && queue.shift()();
270-
271285
const canceled= !!(isRejected && CanceledError.isCanceledError(value));
272286

273-
if(canceled && value.code === E_REASON_UNMOUNTED){
287+
if(canceled && (value.code === E_REASON_UNMOUNTED || value.code === E_REASON_RESTART)){
274288
return;
275289
}
276290

277-
state && singleThreaded && setState({
278-
done: true,
291+
states && singleThreaded && setDeepState({
279292
pending: false,
293+
done: true,
280294
error: isRejected? value : undefined,
281295
result: isRejected? undefined : value,
282296
canceled
283297
});
284298
}).weight(0).aggregate();
285299

286-
promises.push(promise);
300+
promises.push(promise);
287301

288-
combine && argsToPromiseMap.set(promise, args);
302+
combine && argsToPromiseMap.set(promise, args);
289303

290-
return promise;
291-
}
304+
return promise;
305+
}
292306

293-
const promise = resolveGenerator()[catchErrors? 'done' : 'finally'](() => {
294-
removeElement(promises, promise);
295-
combine && argsToPromiseMap.delete(promise);
296-
}).weight(0).aggregate();
307+
const promise = resolveGenerator()[catchErrors? 'done' : 'finally'](() => {
308+
removeElement(promises, promise);
309+
combine && argsToPromiseMap.delete(promise);
310+
}).weight(0).aggregate();
297311

298-
promises.push(promise);
312+
promises.push(promise);
313+
314+
if (combine) {
315+
argsToPromiseMap.set(promise, args);
316+
}
299317

300-
if (combine) {
301-
argsToPromiseMap.set(promise, args);
318+
return promise;
302319
}
303320

304-
return promise;
305-
});
321+
fn.cancel= cancel;
306322

307-
const cancel = (reason) => {
308-
const _reason = isEvent(reason) ? undefined : reason;
309-
current.promises.forEach(promise => promise.cancel(_reason));
310-
current.promises.length = 0;
311-
};
323+
return fn;
324+
}, deps);
325+
326+
useEffect(() => {
327+
return () => callback.cancel(E_REASON_UNMOUNTED);
328+
}, deps);
312329

313330
if(states) {
314331
if(!singleThreaded){
@@ -321,20 +338,14 @@ const useAsyncCallback = (generator, options) => {
321338
callback.canceled = state.canceled;
322339

323340
callback[Symbol.iterator] = function*() {
324-
yield* [callback, cancel, state.pending, state.done, state.result, state.error, state.canceled];
341+
yield* [callback, callback.cancel, state.pending, state.done, state.result, state.error, state.canceled];
325342
}
326343
} else{
327344
callback[Symbol.iterator] = function*() {
328-
yield* [callback, cancel];
345+
yield* [callback, callback.cancel];
329346
}
330347
}
331348

332-
useEffect(() => {
333-
return () => cancel(E_REASON_UNMOUNTED);
334-
}, deps);
335-
336-
callback.cancel = cancel;
337-
338349
return callback;
339350
}
340351

0 commit comments

Comments
 (0)