<p>我正在尝试调用200个远程资源以在我的表格中显示,同时显示一个进度条来显示剩余的调用次数。</p>
<p>使用这个示例来演示如何使用<code>Fetch()</code>和<code>Promise.all()</code>来调用<code>setState()</code>来更新新数据。</p>
<p>我的问题在于每个promise的<code>.then()</code>,它计算一些逻辑,然后调用<code>setState()</code>来更新数据。</p>
<p>我的进度条使用<code>Object.keys(data).length</code>来显示进度。</p>
<p>在<code>Promise.all()</code>触发“完成”状态后,移除进度条,promise本身仍然在调用它们的<code>then()</code>,这导致进度条在显示完所有已解决的promise之前就被隐藏了。</p>
<p>如何正确处理这个问题?</p>
<hr />
<p>演示,使用<code>setTimeout()</code>来模拟昂贵的逻辑。</p>
<p>问题在于<code>Promise.all.then: 20</code>应该在<code>Render 20</code>之后。</p>
<pre class="brush:none;toolbar:false;">Render 0
...
Render 12
Promise.all.then: 20 # 我需要这个在每个Render之后记录
Render 13
...
Render 19
Render 20
</pre>
<p>为了使演示显示出问题,进度条在完全填满之前就被移除(变成红色)。</p>
<p><br />></p>
const { useState } = React;
const 示例 = () => {
const [完成,setDone] = useState(false);
const [数据,setData] = useState({});
const demoData = Array.from(Array(20).keys());
const demoResolver = (x) =>;新的 Promise(res => setTimeout(() => res(x), Math.random() * 1250))
const loadData = () =>; {
const Promise = demoData.map(c => demoResolver(c));
Promise.forEach(promise => {
承诺
.then(r => {
setTimeout(() => {
setData(p => ({ ...p, [r]: r }));
}, 500);
})
});
Promise.all(承诺)
.then(r => {
console.log('Promise.all.then: ', r.length)
设置完成(真);
})
}
console.log('渲染', Object.keys(data).length);
const ProgressBarIsShownDebugColor =(完成)
? '是危险'
: '是信息';
返回 (
<节类名='节'>
{'示例'}
;
<进展
最大值={demoData.length}
value={Object.keys(data).length}
className={'progress my-3' + ProgressBarIsShownDebugColor}
>>
</节>
)
}
ReactDOM.render(<示例/>, document.getElementById("react"));
;
.as-console-wrapper { max-height: 50px !important; }</pre>
<pre class="brush:html;toolbar:false;"><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min .js"></脚本>
<脚本 src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<链接 rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<div id="react"></div></pre>
<p><br />></p>
上面代码中显示的问题是,在获取数据后,在设置状态之前有额外的500ms异步延迟。在实际代码中,听起来有额外的处理(可能是同步的)导致
setData
在.all
之后被调用。最好的做法是将
done
作为一个计算属性而不是一个单独的状态,因为在那个点上,您不需要依赖于状态设置竞争,并且Object.keys(data).length
足够便宜,不会降低性能(而且您在其他区域使用它,如果它成为一个问题,您可以将其缓存到一个变量中)。