我有以下情况,在我的棋盘游戏界面的基础上的反应。
我有一个事件处理程序,它在玩家移动时更新状态:
const move = ...
this.setState(function (state, _props) {
return this.apply_move(state, move);
});
apply_move(state, move) {
let board_copy = _.cloneDeep(state.board);
// applies the move to board_copy somehow
...
return {
board: board_copy,
};
}然后我想先渲染我的移动,然后运行AI为机器人生成一个移动:这个函数调用需要几秒钟,而AI正在计算它的移动。它实际上是调用一个WebAssembly函数。
我把AI移动的请求放在componentDidUpdate里
componentDidUpdate(prevProps, prevState) {
if (this.state.history.length > prevState.history.length) {
if (this.botHasToMove()) {
this.getMoveFromBot(); // <--- this runs for 5 seconds
}
}
}我预计这将在调用render之后运行,所以我将首先看到我的移动呈现,然后在AI计算时暂停几秒钟,然后应该呈现AI移动。
然而,问题是,即使在请求AI移动之前,我在控制台中看到了render的调用,而且我可以看到状态是用我的移动更新的,但是DOM本身并没有被更新。只有当获得AI移动的长调用完成并再次发生render时,它才会被更新。然后,两个动作同时呈现,我可以在浏览器中看到它们。
我希望每个对render的调用都应该更新DOM (如果发生了一些变化,在我的例子中),但是由于某种原因,DOM更新没有发生。
如果我不调用componentDidUpdate中对AI的长调用,一切正常,我可以看到第一个render的效果。
请有人解释一下为什么会发生这种情况,并告诉我在第一个render调用中强制更新DOM的正确方法是什么?
发布于 2020-06-13 01:09:25
我喜欢你提出的问题,因为我喜欢这类问题,你的代码实际上做得很好,但是你的代码有问题,你实际上提到了一个长调用或长执行代码,JS运行在一个线程中,这意味着所有进程都运行在一个通道(线程),所以我强烈建议您观看这两个视频,关于JS中的事件循环,这是您出现问题的原因:
简单地说,您的问题是在JS中阻塞线程执行,我的意思是所有的JS执行都是在一个“执行队列”中顺序执行的,所以下一个执行代码在前面的代码完成之前不会执行。
那你怎么解决这个问题呢?为了避免阻塞线程,您需要在另一个线程中执行代码,但是JS只是一个线程执行语言,所以您确实需要一些名为WebWorkers的东西,它们允许您在另一个“线程”中运行代码,因此您的问题是阻塞执行队列,因为执行函数花费的时间太长,无法解决。
我希望这能对您有很大帮助,并帮助您了解JS事件循环/执行队列的工作原理。
https://stackoverflow.com/questions/62354011
复制相似问题