Skip to content

Commit 3ccd038

Browse files
Fixed bug with useAsyncEffect user cancellation;
1 parent 1cb5f11 commit 3ccd038

File tree

3 files changed

+32
-44
lines changed

3 files changed

+32
-44
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [0.3.0] - 2021-01-07
8+
9+
### Fixed
10+
- Bug with `useAsyncEffect` user cancellation;
11+
712
## [0.2.0] - 2021-01-06
813

914
### Added

lib/use-async-effect.js

+19-40
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,26 @@ const isGeneratorFn = (thing) => typeof thing === 'function' &&
99
thing.constructor &&
1010
thing.constructor.name === 'GeneratorFunction';
1111

12+
const isEvent = (thing) => !!(thing && typeof thing === 'object' && thing.type);
13+
1214
const useAsyncEffect = (generator, deps) => {
13-
let promise;
15+
let ref = useRef(null);
16+
17+
const cancel = (reason) => {
18+
const promise = ref.current;
19+
if (promise) {
20+
ref.current = null;
21+
return promise.cancel(isEvent(reason) ? undefined : reason)
22+
}
23+
return false;
24+
}
1425

1526
useEffect(() => {
1627
if (!isGeneratorFn(generator)) {
1728
throw TypeError('useAsyncEffect requires a generator as the first argument');
1829
}
1930

20-
promise = CPromise.resolveGenerator(generator, {resolveSignatures: true});
31+
let promise = ref.current = CPromise.resolveGenerator(generator, {resolveSignatures: true});
2132

2233
let cb;
2334

@@ -33,45 +44,12 @@ const useAsyncEffect = (generator, deps) => {
3344
});
3445

3546
return () => {
36-
promise.cancel(E_REASON_UNMOUNTED);
37-
promise = null;
47+
cancel(E_REASON_UNMOUNTED);
3848
cb && cb();
3949
}
4050
}, deps);
4151

42-
return (reason) => !!promise && promise.cancel(reason);
43-
}
44-
45-
function asyncBottleneck(fn, concurrency = 1) {
46-
const queue = [];
47-
let pending = 0;
48-
return (...args) => new CPromise(resolve => pending === concurrency ? queue.push(resolve) : resolve())
49-
.then(() => {
50-
pending++;
51-
return fn(...args).finally((value) => {
52-
pending--;
53-
queue.length && queue.shift()();
54-
return value;
55-
})
56-
});
57-
}
58-
59-
function asyncBottleneck2(fn, concurrency = 1) {
60-
const queue = [];
61-
let pending = 0;
62-
return async (...args) => {
63-
if (pending === concurrency) {
64-
await new Promise((resolve) => queue.push(resolve));
65-
}
66-
67-
pending++;
68-
69-
return fn(...args).then((value) => {
70-
pending--;
71-
queue.length && queue.shift()();
72-
return value;
73-
});
74-
};
52+
return cancel;
7553
}
7654

7755
const useAsyncCallback = (generator, options) => {
@@ -103,7 +81,7 @@ const useAsyncCallback = (generator, options) => {
10381
const resolveGenerator = () => CPromise.resolveGenerator(generator, {args, resolveSignatures: true});
10482

10583
if (concurrency) {
106-
const promise= new CPromise(resolve => {
84+
const promise = new CPromise(resolve => {
10785
if (current.pending === concurrency) {
10886
return queue.push(resolve);
10987
}
@@ -125,14 +103,15 @@ const useAsyncCallback = (generator, options) => {
125103
cancel();
126104
}
127105

128-
const promise= resolveGenerator().finally(()=>{
106+
const promise = resolveGenerator().finally(() => {
129107
const index = promises.indexOf(promise);
130108
index !== -1 && promises.splice(index, 1);
131109
});
132110

133111
promises.push(promise);
134112

135-
return combine? promise.finally(()=>{}) : promise;
113+
return combine ? promise.finally(() => {
114+
}) : promise;
136115
});
137116

138117
const cancel = (reason) => {

playground/src/TestComponent1.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import cpFetch from "cp-fetch";
77
export default function TestComponent1(props) {
88
const [text, setText] = useState("");
99

10-
const cancel= useAsyncEffect(function* ({onCancel}) {
10+
const cancel = useAsyncEffect(function* ({onCancel}) {
1111
console.log("mount");
1212

13-
this.timeout(5000);
13+
this.timeout(10000);
1414

15-
onCancel(()=> console.log('scope canceled'));
15+
onCancel(() => console.log('scope canceled'));
1616

1717
try {
1818
setText("fetching...");
@@ -31,6 +31,10 @@ export default function TestComponent1(props) {
3131

3232
//setTimeout(()=> cancel("Ooops!"), 1000);
3333

34-
return <div className="component"><div className="caption">useAsyncEffect demo:</div><div>{text}</div></div>;
34+
return <div className="component">
35+
<div className="caption">useAsyncEffect demo:</div>
36+
<div>{text}</div>
37+
<button onClick={cancel}>Abort</button>
38+
</div>;
3539
}
3640

0 commit comments

Comments
 (0)