JavaScript中如何优雅处理动画与数据请求的竞态条件
优雅处理动画与异步请求的同步问题
这个场景太常见了!之前做项目的时候也被这种「谁先完成谁等谁」的异步同步问题坑过,用标志位判断确实容易绕晕,用Promise来包装两个异步事件再同步等待是最干净的解决方案,哪怕你没法修改原有回调的逻辑也完全可行。
核心思路
把两个异步操作(动画结束、数据返回)都包装成Promise,然后用Promise.all()等待两者都完成后,再执行UI重新渲染——这样不管谁先完成,都会自动等另一个,完全不用手动维护状态标志。
具体代码实现
先把原有的回调式操作转换成Promise,兼容你不能修改原有回调的限制:
// 1. 把fadeOut动画包装成Promise function fadeOutAsPromise() { return new Promise(resolve => { // 调用原动画函数,把Promise的resolve作为完成回调传入 startFadeOutAnimation(resolve); }); } // 2. 把数据请求包装成Promise(适配原有不可修改的回调) let resolveDataPromise; const dataPromise = new Promise(resolve => { // 把Promise的resolve存到外部变量,方便在原有回调里触发 resolveDataPromise = resolve; }); // 改造原有数据回调(不用修改原有业务逻辑,只加一行触发Promise的代码) function callBackFunctionToRecevieDataFromService(data) { var receivedData = data; // 触发我们的Promise,把拿到的数据传出去 resolveDataPromise(data); // 保留原有的逻辑(如果有需要的话,同步判断的部分可以删掉了) // if(animationStillGoingOn) { return; } else { commonFunction(); } } // 按钮点击事件的新逻辑 function onButtonClick() { // 同时启动动画和数据请求 const animationDone = fadeOutAsPromise(); doSomeServiceCall(); // 原有的请求函数完全不用改 // 等待两个异步操作全部完成 Promise.all([animationDone, dataPromise]) .then(([_, receivedData]) => { // 这里就是两者都完成后的统一逻辑,直接渲染UI performReRenderOfUIComponent(receivedData); }) .catch(err => { // 可以加个错误处理,比如请求失败或动画出错的情况 console.error('操作失败:', err); }); }
为什么这比原方案好?
- 没有冗余状态变量:不用再维护
animationStillGoingOn、receivedData这些容易混乱的标志位,Promise会自动管理异步状态 - 逻辑更直观:一眼就能看懂是「等动画和数据都准备好再渲染」,不用绕弯子看
commonFunction里的嵌套判断 - 扩展性强:如果以后要加第三个异步操作(比如另一个过渡动画),直接加到
Promise.all的数组里就行
内容的提问来源于stack exchange,提问作者Ryan Gusto
相关产品推荐
相关产品推荐

