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

Vue异步方法仍阻塞主线程?easystarjs路径计算卡顿DOM求解

问题分析与解决方案

你确实误解了Promise的作用——Promise不会将同步代码转换为异步执行。它只是提供了一种更优雅的方式处理异步回调,或者将回调式的异步任务包装成Promise风格,但如果Promise内部的代码是同步阻塞的(比如你的props.easystar.calculate()),那它依然会占用主线程,导致DOM冻结。

你的代码里,calcPath函数虽然返回了Promise,但easystar.calculate()是同步执行的路径计算逻辑,这部分会在主线程跑2-3秒,完全阻塞UI渲染,所以Promise并没有起到异步非阻塞的作用。


两种解决思路

1. 使用Web Worker将计算移到后台线程

Web Worker运行在独立的后台线程,不会占用主线程,适合处理这种耗时的计算任务。

主线程代码修改:

<script setup>
const findPath = () => {
  console.log("findPath-before");
  
  // 创建Worker实例(需提前创建path-worker.js文件)
  const pathWorker = new Worker('./path-worker.js');
  
  // 向Worker发送计算所需的参数
  pathWorker.postMessage({
    start: props.party.pos,
    destination: props.party.destinations[0],
    acceptableTiles: [1, 0],
    plainTileCost: 4,
    grid: /* 这里传入你的地图网格数据 */
  });
  
  // 接收Worker返回的计算结果
  pathWorker.onmessage = (event) => {
    const path = event.data;
    if (path === null) {
      alert("Path was not found.");
    } else {
      props.party.path = { current: 0, data: path };
      console.log(true);
    }
    // 计算完成后销毁Worker
    pathWorker.terminate();
  };
  
  console.log("findPath-after");
};
</script>

<template>
  <Button @click="findPath">
    <span>Load path</span>
  </Button>
</template>

Worker文件(path-worker.js):

// 引入easystar库(注意路径要正确)
importScripts('lib/easystar.min.js');

self.onmessage = (event) => {
  const { start, destination, acceptableTiles, plainTileCost, grid } = event.data;
  
  const easystar = new EasyStar.js();
  easystar.setGrid(grid);
  easystar.setAcceptableTiles(acceptableTiles);
  easystar.setTileCost(plainTileCost, 4);
  
  easystar.findPath(
    start.x, start.y,
    destination.x, destination.y,
    (path) => {
      // 将计算结果发送回主线程
      self.postMessage(path);
    }
  );
  
  easystar.calculate();
};

2. 使用easystar的分帧计算功能

easystar提供了setIterationsPerCalculation方法,可以将路径计算拆分成多个小批次执行,每次只运行指定次数的迭代,这样不会一次性阻塞主线程,给DOM渲染留出生存空间。

修改你的calcPath函数:

<script setup>
const findPath = async () => {
  console.log("findPath-before");
  const result = await calcPath();
  console.log(result);
  console.log("findPath-after");
};

const calcPath = () => {
  return new Promise((resolve) => {
    if (
      props.party.path ||
      (props.party.pos.x === props.party.destinations[0].x &&
        props.party.pos.y === props.party.destinations[0].y)
    ) {
      resolve(false);
      return;
    }

    props.easystar.setAcceptableTiles([1, 0]);
    props.easystar.setTileCost(Terrain.Plain, 4);
    // 设置每次计算的迭代次数,数值越小越不阻塞UI,可根据地图大小调整
    props.easystar.setIterationsPerCalculation(100);
    
    props.easystar.findPath(
      props.party.pos.x,
      props.party.pos.y,
      props.party.destinations[0].x,
      props.party.destinations[0].y,
      (path) => {
        if (path === null) {
          alert("Path was not found.");
          resolve(false);
        } else {
          props.party.path = { current: 0, data: path };
          resolve(true);
        }
      }
    );
    
    // 异步分帧计算,不会阻塞主线程
    props.easystar.calculate();
  });
};
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 15:25:30