
本文介绍如何在 React 中通过 Promise 链式调用,精确控制多个动画函数的执行顺序,避免因异步 setTimeout 未等待完成就触发下一轮更新而导致的竞态问题。
本文介绍如何在 react 中通过 promise 链式调用,精确控制多个动画函数的执行顺序,避免因异步 `settimeout` 未等待完成就触发下一轮更新而导致的竞态问题。
在 React 开发中,当需要按序执行多个依赖时间的 UI 动画(如高亮路径节点、逐帧渲染搜索过程)时,直接连续调用函数或使用固定毫秒数的 setTimeout 往往不可靠——因为动画实际持续时间取决于节点数量、渲染性能及 React 批处理机制,硬编码延迟极易失准。
最健壮的解决方案是将动画逻辑封装为返回 Promise 的函数,并利用 Promise.all() 汇总所有帧更新任务,确保整组动画真正全部提交并完成状态变更后再通知上层链式执行。
以下是改造后的 animate 函数实现:
const animate = (nodes: Node[]) => {
const promises: Promise<void>[] = [];
for (let i = 0; i ((resolve) => {
setTimeout(() => {
const node = nodes[i];
setTable((prev) => {
// 安全深拷贝 grid(避免直接修改)
const newArray = prev.grid.map(row => [...row]);
const updatedGrid = newArray.map((items) =>
items.map((item) => {
const { row, col } = item.pos;
return row === node.row && col === node.col
? { ...item, isAnimate: !item.isAnimate }
: item;
})
);
const nextState = {
...prev,
grid: updatedGrid,
};
resolve(); // ✅ 关键:状态更新完成后才 resolve
return nextState;
});
}, 10 * i); // 每帧间隔 10ms,形成流畅序列
})
);
}
// 返回 Promise.all,代表整个动画序列完成
return Promise.all(promises);
};</void>
随后,在按钮点击事件中即可清晰表达执行顺序:
Orderly React SDK 钩子使用参考指南,包括 useOrderEntry、usePositionStream、useOrderbookStream、useCollateral 等。
<button onclick="{()"> {
animate(nodesVisitedInOrder)
.then(() => animate(path))
.catch(console.error); // 建议添加错误处理
}}
>
Start Animation
</button>
✅ 关键要点说明:
-
Promise.all(promises)确保所有setTimeout回调都已触发且setTable的状态更新回调执行完毕(注意:resolve()放在setTable的 updater 函数内部,而非setTimeout外部,以准确捕捉状态提交时机); - 避免使用
useEffect监听table变化来触发后续动画——这会引入不必要的副作用循环与时机不确定性; - 若动画需支持中断(如用户快速连点),可在
animate内维护一个abortController或isCancelled标志位,resolve前校验状态; - 对于更复杂的交互动画(如 CSS transitions / animations),应监听
animationend或transitionend事件并封装为 Promise,原理一致。
通过 Promise 化异步操作,你不仅实现了可靠的执行时序,还让代码具备了可组合性与可测试性——例如可轻松扩展为 animate(A).then(() => delay(500)).then(() => animate(B))。










