Skip to content

Commit 071b3a2

Browse files
Fixed queueSize bug;
1 parent 9cf2616 commit 071b3a2

File tree

4 files changed

+216
-100
lines changed

4 files changed

+216
-100
lines changed

lib/use-async-effect.js

+25-19
Original file line numberDiff line numberDiff line change
@@ -227,36 +227,42 @@ const useAsyncCallback = (generator, options) => {
227227
}
228228
}
229229

230-
if (queueSize !== -1 && (queueSize - promises.length - current.pending) <= 0) {
231-
return CPromise.reject(CanceledError.from(E_REASON_QUEUE_OVERFLOW))
232-
}
233-
234230
const resolveGenerator = () => CPromise.run(generator, {args, resolveSignatures: true, scopeArg});
235231

236232
cancelPrevious && !combine && cancel(E_REASON_RESTART);
237233

238-
if (threads || queueSize!==-1) {
234+
if (threads || queueSize !== -1) {
235+
let started;
236+
239237
const promise = new CPromise(resolve => {
240-
if (current.pending === threads) {
241-
return queue.push(() => {
242-
current.pending++;
243-
resolve()
244-
});
238+
const start= ()=> {
239+
current.pending++;
240+
241+
started= true;
242+
243+
if (states && singleThreaded) {
244+
setState({
245+
done: false,
246+
pending: true,
247+
canceled: false
248+
})
249+
}
250+
resolve();
245251
}
246-
current.pending++;
247252

248-
if(states && singleThreaded){
249-
setState({
250-
done: false,
251-
pending: true,
252-
canceled: false
253-
})
253+
if (current.pending === threads) {
254+
if (queueSize !== -1 && queue.length === queueSize) {
255+
throw CanceledError.from(E_REASON_QUEUE_OVERFLOW);
256+
}
257+
258+
return queue.push(start);
254259
}
255-
resolve();
260+
261+
start();
256262
}).weight(0)
257263
.then(resolveGenerator)
258264
.finally((value, isRejected) => {
259-
current.pending--;
265+
started && current.pending--;
260266
removeElement(promises, promise);
261267
combine && argsToPromiseMap.delete(promise);
262268
queue.length && queue.shift()();

playground/src/App.js

+70-36
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,74 @@ import TestComponent5 from "./TestComponent5";
77
import TestComponent6 from "./TestComponent6";
88

99
export default class App extends React.Component {
10-
constructor(props) {
11-
super(props);
12-
this.state = {url: props.url, _url: props.url};
13-
this.onClick = this.onClick.bind(this);
14-
this.onFetchClick = this.onFetchClick.bind(this);
15-
this.handleChange= this.handleChange.bind(this);
16-
}
17-
18-
onFetchClick(){
19-
this.setState(({_url})=> this.setState({url: _url}))
20-
}
21-
22-
onClick() {
23-
this.setState({ timestamp: Date.now() });
24-
}
25-
26-
handleChange(event){
27-
console.log(event.target.value);
28-
this.setState({_url: event.target.value});
29-
}
30-
31-
render() {
32-
return (
33-
<div key={this.state.timestamp} id="app">
34-
<input className="field-url" type="text" value={this.state._url} onChange={this.handleChange} />
35-
<button className="btn-change" onClick={this.onFetchClick} disabled={this.state.url==this.state._url}>Change URL to Fetch</button>
36-
<TestComponent1 url={this.state.url}></TestComponent1>
37-
<TestComponent2 url={this.state.url}></TestComponent2>
38-
<TestComponent3 url={this.state.url}></TestComponent3>
39-
<TestComponent4 url={this.state.url}></TestComponent4>
40-
<TestComponent5 url={this.state.url}></TestComponent5>
41-
<TestComponent6 url={this.state.url}></TestComponent6>
42-
<div><button onClick={this.onClick}>Remount all components</button></div>
43-
</div>
44-
);
45-
}
10+
constructor(props) {
11+
super(props);
12+
this.state = { url: props.url, _url: props.url, timeout: 4500 };
13+
this.onClick = this.onClick.bind(this);
14+
this.onFetchClick = this.onFetchClick.bind(this);
15+
this.handleChange = this.handleChange.bind(this);
16+
this.handleTimeoutChange = this.handleTimeoutChange.bind(this);
17+
}
18+
19+
onFetchClick() {
20+
this.setState(({ _url }) => ({ url: _url }));
21+
}
22+
23+
onClick() {
24+
this.setState({ timestamp: Date.now() });
25+
}
26+
27+
handleChange(event) {
28+
console.log(event.target.value);
29+
this.setState({ _url: event.target.value });
30+
}
31+
32+
handleTimeoutChange(event) {
33+
this.setState({ timeout: event.target.value * 1 });
34+
}
35+
36+
render() {
37+
return (
38+
<div key={this.state.timestamp} id="app">
39+
<input
40+
className="field-url"
41+
type="text"
42+
value={this.state._url}
43+
onChange={this.handleChange}
44+
/>
45+
<button
46+
className="btn btn-info btn-change"
47+
onClick={this.onFetchClick}
48+
disabled={this.state.url === this.state._url}
49+
>
50+
Change URL to Fetch
51+
</button>
52+
<div>
53+
<input
54+
id="timeout-input"
55+
type="range"
56+
min="5"
57+
max="5000"
58+
value={this.state.timeout}
59+
onChange={this.handleTimeoutChange}
60+
step="1"
61+
/>
62+
<label htmlFor="timeout-input">
63+
Timeout [{this.state.timeout}]ms
64+
</label>
65+
</div>
66+
<TestComponent1 url={this.state.url} timeout={this.state.timeout}></TestComponent1>
67+
<TestComponent2 url={this.state.url} timeout={this.state.timeout}></TestComponent2>
68+
<TestComponent3 url={this.state.url} timeout={this.state.timeout}></TestComponent3>
69+
<TestComponent4 url={this.state.url} timeout={this.state.timeout}></TestComponent4>
70+
<TestComponent5 url={this.state.url} timeout={this.state.timeout}></TestComponent5>
71+
<TestComponent6 url={this.state.url} timeout={this.state.timeout}></TestComponent6>
72+
<button className="btn btn-danger" onClick={this.onClick}>
73+
Remount component
74+
</button>
75+
</div>
76+
);
77+
}
4678
}
79+
80+

playground/src/TestComponent6.js

+89-45
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import { useAsyncCallback } from "../../lib/use-async-effect";
33
import { CPromise } from "c-promise2";
44

55
function TestComponent(props) {
6-
const [combine, setCombine]= useState(false);
7-
const [cancelPrevious, setCancelPrevious]= useState(false);
8-
const [randomDelay, setRandomDelay]= useState(false);
9-
const [list, setList]= useState([]);
6+
const [combine, setCombine] = useState(false);
7+
const [cancelPrevious, setCancelPrevious] = useState(false);
8+
const [randomDelay, setRandomDelay] = useState(false);
9+
const [queueSize, setQueueSize] = useState(-1);
10+
const [list, setList] = useState([]);
1011

1112
const [callback, cancel, pending, done, result, err] = useAsyncCallback(
1213
function* (...args) {
14+
this.timeout(props.timeout);
1315
console.log(`start [${args}]`);
1416
yield CPromise.delay(randomDelay ? 2000 + Math.random() * 5000 : 4000);
1517
if (args[1]) {
@@ -18,46 +20,57 @@ function TestComponent(props) {
1820
console.log(`end [${args}]`);
1921
return new Date().toLocaleTimeString();
2022
},
21-
{ states: true, deps: [props.url, combine, cancelPrevious, randomDelay], combine, cancelPrevious}
23+
{
24+
states: true,
25+
deps: [props.url, combine, cancelPrevious, randomDelay, queueSize],
26+
combine,
27+
cancelPrevious,
28+
queueSize
29+
}
2230
);
2331

24-
const pushTask= (...args)=> {
25-
const promise= callback(...args).catch(err=>{
26-
setList(list=> list.map(item=> item.task===promise? {
27-
...item,
28-
title: `Task with [${args}] argument failed ${err.toString()}`,
29-
err
30-
} : item));
31-
return CPromise.delay(2000);
32-
}).then(()=>{
33-
setList((list)=> list.filter(entry=> promise!==entry.task))
34-
})
35-
setList(list=> [...list, {
36-
title: `Task with [${args}] argument queued at ${new Date().toLocaleTimeString()}`,
37-
arg: args,
38-
task: promise
39-
}])
40-
}
32+
const pushTask = (...args) => {
33+
const promise = callback(...args)
34+
.catch((err) => {
35+
setList((list) =>
36+
list.map((item) =>
37+
item.task === promise
38+
? {
39+
...item,
40+
title: `Task with [${args}] argument failed ${err.toString()}`,
41+
err
42+
}
43+
: item
44+
)
45+
);
46+
return CPromise.delay(3000);
47+
})
48+
.then(() => {
49+
setList((list) => list.filter((entry) => promise !== entry.task));
50+
});
51+
setList((list) => [
52+
...list,
53+
{
54+
title: `Task with [${args}] argument queued at ${new Date().toLocaleTimeString()}`,
55+
arg: args,
56+
task: promise
57+
}
58+
]);
59+
};
4160

4261
return (
4362
<div className="component">
4463
<div className="caption">useAsyncCallback combine demo:</div>
4564
<div>
46-
{done ? (
47-
err ? (
48-
err.toString()
49-
) : (
50-
result
51-
)
52-
) : (
53-
pending? 'pending...' : ''
54-
)}
65+
{done ? (err ? err.toString() : result) : pending ? "pending..." : ""}
5566
</div>
56-
<button onClick={()=> pushTask('🍊')}>Make a call with 🍊</button>
57-
<button onClick={()=> pushTask('🍓')}>Make a call with 🍓</button>
58-
<button onClick={()=> pushTask('🍏')}>Make a call with 🍏</button>
59-
<button onClick={()=> pushTask('🍑')}>Make a call with 🍑</button>
60-
<button onClick={()=> pushTask('🍇', new Error('my error'))}>Make a call with 🍇 that will fail after 4000ms</button>
67+
<button onClick={() => pushTask("🍊")}>Make a call with 🍊</button>
68+
<button onClick={() => pushTask("🍓")}>Make a call with 🍓</button>
69+
<button onClick={() => pushTask("🍏")}>Make a call with 🍏</button>
70+
<button onClick={() => pushTask("🍑")}>Make a call with 🍑</button>
71+
<button onClick={() => pushTask("🍇", new Error("my error"))}>
72+
Make a call with 🍇 that will fail after 4000ms
73+
</button>
6174
<button className="btn btn-danger" onClick={cancel} disabled={!pending}>
6275
Cancel all running calls
6376
</button>
@@ -67,37 +80,68 @@ function TestComponent(props) {
6780
<li>
6881
<label>
6982
combine
70-
<input type="checkbox" onChange={({target}) => setCombine(target.checked)}/>
83+
<input
84+
type="checkbox"
85+
onChange={({ target }) => setCombine(target.checked)}
86+
/>
7187
</label>
7288
</li>
7389
<li>
7490
<label>
7591
cancelPrevious
76-
<input type="checkbox" onChange={({target}) => setCancelPrevious(target.checked)}/>
92+
<input
93+
type="checkbox"
94+
onChange={({ target }) => setCancelPrevious(target.checked)}
95+
/>
96+
</label>
97+
</li>
98+
<li>
99+
<label>
100+
Queue size:&nbsp;
101+
<input
102+
type="number"
103+
min="-1"
104+
max="100"
105+
value={queueSize}
106+
step="1"
107+
onChange={({ target }) => setQueueSize(target.value * 1)}
108+
/>
77109
</label>
78110
</li>
79111
<li>
80112
<label>
81113
use random delay for tasks
82-
<input type="checkbox" onChange={({target}) => setRandomDelay(target.checked)}/>
114+
<input
115+
type="checkbox"
116+
onChange={({ target }) => setRandomDelay(target.checked)}
117+
/>
83118
</label>
84119
</li>
85120
</ul>
86121
</div>
87122
<div>
88123
Requested calls [{list.length}]:
89124
<ul>
90-
{list.map(({title, err, task})=> <li style={{color: err && 'red'}}>{title}&nbsp;
91-
{!err && <button onClick={()=>{
92-
task.cancel();
93-
}}></button>}
94-
</li>)}
125+
{list.map(({ title, err, task }) => (
126+
<li style={{ color: err && "red" }}>
127+
{title}&nbsp;
128+
{!err && (
129+
<button
130+
onClick={() => {
131+
task.cancel();
132+
}}
133+
>
134+
135+
</button>
136+
)}
137+
</li>
138+
))}
95139
</ul>
96140
</div>
97-
98141
</div>
99142
);
100143
}
101144

102145
export default TestComponent;
103146

147+

0 commit comments

Comments
 (0)