You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust与WASM:如何在不阻塞Canvas重绘的情况下休眠X秒?

问题解答

首先明确:不存在让浏览器主线程同步阻塞同时不阻止Canvas重绘的方法。这是由浏览器的单线程模型决定的——主线程负责JavaScript执行、UI渲染(包括Canvas重绘)、事件处理等所有任务,这些任务按顺序在任务队列中执行。一旦你的同步循环阻塞了主线程,渲染任务根本没有机会被调度执行,Canvas自然无法更新。

针对你不想大量修改现有代码的需求,可以尝试以下折中方案,尽量减少移植工作量:

方案1:用requestAnimationFrame改造wait函数(回调式)

把原来的阻塞式wait改成基于帧回调的非阻塞版本,不需要使用async/await:

function wait(ms, callback) {
  const startTime = performance.now();
  function checkDelay() {
    if (performance.now() - startTime >= ms) {
      callback(); // 延迟结束后执行后续逻辑
    } else {
      requestAnimationFrame(checkDelay); // 每帧检查一次,不阻塞主线程
    }
  }
  requestAnimationFrame(checkDelay);
}

// 调用示例(替代原同步代码)
wait(1000, () => {
  // 原wait之后的逻辑放在这里
  updateGameState();
  renderCanvas();
});

方案2:用生成器模拟同步风格逻辑

如果想保留接近原代码的顺序写法,可以用生成器函数把游戏逻辑拆分为可分步执行的片段,避免回调嵌套:

// 改造wait为返回检查函数
function wait(ms) {
  const startTime = performance.now();
  return () => performance.now() - startTime >= ms;
}

// 游戏逻辑写成生成器
function* gameLogic() {
  // 原代码:wait(1000); doA();
  yield wait(1000);
  doA();

  // 原代码:wait(500); doB();
  yield wait(500);
  doB();
}

// 驱动生成器执行的循环
const logicIterator = gameLogic();
function runLogic() {
  const { done, value } = logicIterator.next();
  if (!done) {
    if (value()) { // 检查延迟是否结束
      runLogic();
    } else {
      requestAnimationFrame(runLogic); // 未结束则下一帧再检查
    }
  }
}

// 启动逻辑
requestAnimationFrame(runLogic);

这两种方案都不需要使用async/await,且能让Canvas在等待期间正常重绘——因为它们不会阻塞主线程,而是把等待逻辑分散到每一帧的空闲时间执行。

内容的提问来源于stack exchange,提问作者h2k1dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 08:33:30