在一个React应用程序中,在执行类似于以下简化示例的搜索之前,我们将删除键盘输入:
function App() {
const inputRef = useRef();
const [result, setResult] = useState();
const timeout = useRef();
useEffect(() => () => clearTimeout(timeout.current));
const debouncedSearch = useCallback((text) => {
clearTimeout(timeout.current);
const p = new Promise((resolve, reject) => {
timeout.current = setTimeout(async () => {
const result = await axios.get("/search?q=" + text);
resolve(result);
}, 1500);
});
return p;
}, []);
const onChange = useCallback(() => {
const fn = async () => {
const result = await debouncedSearch(inputRef.current.value);
//if the promise isn't fulfilled will this hold onto setResult?
setResult(result);
};
fn();
}, []);
return (
<div>
<input ref={inputRef} onChange={onChange} />
<div>{result}</div>
</div>
);
}正如您所看到的,在onChange中,我们正在创建一个新的承诺,如果在1.5秒内调用之前的处理程序,那么您可以看到。
既然前面的承诺是创建的,但它们的状态仍然没有实现,那么它们是否会对setResult进行硬引用,后者也比以前的呈现状态更倾向于泄漏内存?
发布于 2022-02-01 03:51:35
您可以使用AbortController和throw语句以及async functions来减少程序执行上下文的数量。throw语句将导致对异步函数的返回承诺的拒绝,在另一个异步函数中等待该承诺将将允诺拒绝转换为异常,您可以使用try...catch来防止异常出现。这将使所有承诺得以兑现,防止潜在的相关内存泄漏。
在本例中,我还为您的
axios调用使用了一个模拟函数,调整了示例的返回值,并减少了debouce延迟(因为1500是一个很长时间的)。
<div id="root"></div><script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.16.12/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">
const {useCallback, useEffect, useRef, useState} = React;
function wait (delayMs: number) {
return new Promise(res => setTimeout(res, delayMs));
}
async function mockAxios (...params: Parameters<(typeof axios)['get']>) {
await wait(30);
return {data: params[0]};
}
function Example () {
const acRef = useRef(new AbortController());
const [result, setResult] = useState<string>();
useEffect(() => () => acRef.current.abort(), []);
const debouncedSearch = useCallback(async (text: string, delayMs: number) => {
acRef.current.abort();
const ac = new AbortController();
acRef.current = ac;
await wait(delayMs);
if (ac.signal.aborted) throw new Error('Aborted');
const url = `/search?q=${text}`;
const {signal} = ac;
const result = await mockAxios(url, {signal});
// const result = await axios.get(url, {signal});
return result.data;
}, []);
const onChange = useCallback(async (ev: React.ChangeEvent<HTMLInputElement>) => {
try {
const result = await debouncedSearch(ev.target.value, 1000);
setResult(result);
}
catch {/* Intentionally empty */}
}, [debouncedSearch]);
return (
<div>
<input {...{onChange}} placeholder="test by typing here" />
<div>{result}</div>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
</script>
https://stackoverflow.com/questions/70922887
复制相似问题