I am trying to get a game to refresh canvas regularly with a main loop comprising a function to replay a screen and a function to have the computer calculate a move (if it is its turn). The player can select a move 1-9 and then, if the computer has not gone since the player last made a move, it will calculate its move using a very deep tree. The computer move takes several seconds to make its move. Despite having the canvas update before the computer makes a move, it doesn't ever get refreshed until after the computer has gone, meaning both the player and the computer move are shown simultaneously rather than sequentially. What can I do to ensure the canvas is updated every 100 milliseconds and not just after when the computer has calculated its move?
function playGame() {
function doKeyDown(evt) {
console.log(evt.keyCode)
if (evt.keyCode > 48 && evt.keyCode < 58) {
game.makeMove(evt.keyCode-48); // 1-9 are user actions
}
}
function mainLoop(ctx, game, board) {
board.showBoard(ctx); // display board
game.makeMove(0); // zero is computer move
}
Canvas.canvas = document.getElementById("myCanvas");
let game = new Game();
let board = new Board(game);
window.addEventListener( "keydown", doKeyDown, true);
Canvas.ctx = Canvas.canvas.getContext('2d');
setInterval(() => {mainLoop(Canvas.ctx, game, board)}, 100);
};
document.addEventListener('DOMContentLoaded', playGame);
There are a few things that can definitely help you with ensuring your canvas refreshes when you expect.
move computationally intensive tasks into Web Workers.
utilize window.requestAnimationFrame() instead of setInterval() for rendering.
Web Workers
Web Workers are great for moving computationally intensive tasks into; the MDN documentation for Web Workers states that, "The worker thread can perform tasks without interfering with the user interface." This will allow the computer's move to be calculated without blocking the rendering thread.
For more information on Web Workers see: Web Workers (MDN Web Docs)
window.requestAnimationFrame()
The window.requestAnimationFrame() method is the best current method to achieve animations with. Essentially it causes the browser to execute the provided callback just before the next repaint. This is an improvement over the previous setTimeout() and setInterval() methods as it only calls the callback just before the browser repaint, in other words, it reduces unnecessary computations that can be caused by using setTimeout() and setInterval().
For more information on window.requestAnimationFrame() see: window.requestAnimationFrame() (MDN Web Docs)
For a nice short article on why to choose window.requestAnimationFrame() over setTimeout() and setInterval() see this article by Matt West: Efficient Animations with requestAnimationFrame
Working Example:
I have put together a small working example using codesandbox.io; it uses Web Workers and requestAnimationFrame() as I have described. Note that it uses a timer in place of a computationally intensive task, it uses a 5 second timer to simulate a computer move, and a 1 second timer to simulate a player move.
Code Sandbox: Web Worker & requestAnimationFrame() Example