如何不使用async实现JS休眠?解决多函数并发执行异常问题
解决原生JS多按键同时触发的移动问题
你的核心问题出在两个地方:全局变量导致的异步函数冲突,以及用async while循环处理持续状态的方式错误。下面直接给你修正方案:
问题分析
- 全局变量
i的冲突:每次按w都会执行FunctionA(),而i是全局变量,新调用的FunctionA会把i重置为0,导致之前还在运行的异步循环逻辑混乱,看起来像是"重启"。 - 错误的循环方式:
async while循环不适合处理持续的按键状态,因为它是一次性的异步任务,无法灵活响应按键的按下/释放状态,而且多个异步任务并行时很难协调。
正确实现方案
应该用按键状态跟踪 + requestAnimationFrame帧更新的模式,这是前端处理连续交互(比如移动)的标准做法:
// 跟踪按键状态的对象 const keyStates = { w: false, a: false }; // 目标元素(假设你要移动的是这个元素,可根据实际修改) let targetElement; document.addEventListener('DOMContentLoaded', () => { 'use strict'; targetElement = document.getElementById('target'); // 替换成你的元素ID // 按键按下时标记状态为true document.addEventListener('keydown', event => { const key = event.key.toLowerCase(); if (keyStates.hasOwnProperty(key)) { keyStates[key] = true; event.preventDefault(); // 防止浏览器默认滚动等行为 } }); // 按键释放时标记状态为false document.addEventListener('keyup', event => { const key = event.key.toLowerCase(); if (keyStates.hasOwnProperty(key)) { keyStates[key] = false; event.preventDefault(); } }); // 帧更新函数,每一帧检查按键状态并执行移动 function update() { let moveX = 0; let moveY = 0; // 根据按键状态计算移动方向 if (keyStates.w) moveY -= 2; // w键向上移动(Y轴减小) if (keyStates.a) moveX -= 2; // a键向左移动(X轴减小) // 应用移动到元素 if (moveX !== 0 || moveY !== 0) { const currentX = parseInt(window.getComputedStyle(targetElement).left) || 0; const currentY = parseInt(window.getComputedStyle(targetElement).top) || 0; targetElement.style.left = `${currentX + moveX}px`; targetElement.style.top = `${currentY + moveY}px`; } // 持续请求下一帧 requestAnimationFrame(update); } // 启动帧更新 requestAnimationFrame(update); });
配套CSS示例(用于测试)
#target { position: absolute; width: 50px; height: 50px; background-color: blue; left: 100px; top: 100px; }
关键改进点
- 按键状态跟踪:用
keyStates对象记录每个键的按下/释放状态,不会因为重复触发keydown而重复启动任务,而是统一在帧更新中处理。 - requestAnimationFrame:浏览器原生的帧同步API,确保移动逻辑和浏览器渲染节奏一致,避免卡顿,同时天然支持多按键状态的并行处理。
- 无全局变量污染:所有状态变量都在作用域内,不会出现异步任务之间的变量冲突。
如果你坚持要用async/await的方式(不推荐,因为不适合这种场景),至少要把i改成函数内的局部变量,并且用一个标志位控制循环是否继续:
const sleep = (delayInms) => { return new Promise(resolve => setTimeout(resolve, delayInms)); }; // 用对象跟踪FunctionA的运行状态 const functionAState = { isRunning: false }; async function FunctionA() { if (functionAState.isRunning) return; // 如果已经在运行,直接返回 functionAState.isRunning = true; let i = 0; // 局部变量,避免全局冲突 while (i < 50) { await sleep(10); console.log("A"); i++; } functionAState.isRunning = false; } function FunctionB() { console.log("B"); } document.addEventListener('DOMContentLoaded', () => { 'use strict'; document.addEventListener('keydown', event => { const key = event.key.toLowerCase(); if (key === "w") { FunctionA(); } if (key === "a") { FunctionB(); } }); });
但这种方式依然无法很好地处理"按住w同时按a"的持续移动场景,还是推荐第一种帧更新的方案。
内容的提问来源于stack exchange,提问作者Scrafty-IMB
相关产品推荐
相关产品推荐

