Web Animation API:animation.cancel()后animation.finished Promise始终pending
Web Animation API cancel()后finished Promise pending问题解决方案
调用animation.cancel()后,对应的finished Promise不会保持pending,而是会抛出AbortError并reject。你的代码中依赖Promise.all([ani && ani.finished])等待上一个动画完成,但未处理reject情况,导致Promise链卡住,后续动画无法执行。
核心原因
- 动画正常结束时,
finishedPromise会resolve;调用cancel()强制终止动画时,finishedPromise会直接reject,而非持续pending。 - 原代码未处理reject分支,导致
Promise.all的回调永远不会触发,新动画无法创建。
解决方案
根据你“点击按钮时,等待上一个动画结束后切换到新元素执行动画”的需求,提供两种调整方案:
方案1:等待上一个动画完成(或处理cancel)后启动新动画
使用async/await简化Promise逻辑,同时捕获cancel导致的reject,避免阻塞:
let btn = document.querySelector("#btn"); let element = null; let currentId = 1; let ani = null; let transformArray = [ {transform: 'translate(0px, 0px)'}, {transform: 'translate(100px, 0px)'} ]; btn.onclick = async function () { // 处理上一个动画 if (ani) { try { // 等待上一个动画正常完成 await ani.finished; } catch (err) { // 捕获cancel导致的reject,避免后续逻辑阻塞 console.log('上一个动画被取消', err); } finally { // 可选:取消动画以重置元素状态(如果不需要保持最终状态) ani.cancel(); } } // 切换到当前元素并启动新动画 element = document.querySelector("#icon" + currentId); ani = element.animate(transformArray, { duration: 1000, fill: "forwards", easing: "ease" }); // 更新元素ID循环 currentId++; if (currentId === 4) { currentId = 1; } };
方案2:立即取消上一个动画并启动新动画
如果不需要等待上一个动画完成,直接切换元素执行动画:
let btn = document.querySelector("#btn"); let element = null; let currentId = 1; let ani = null; let transformArray = [ {transform: 'translate(0px, 0px)'}, {transform: 'translate(100px, 0px)'} ]; btn.onclick = function () { // 立即取消上一个动画(如果存在) ani?.cancel(); // 启动当前元素的动画 element = document.querySelector("#icon" + currentId); ani = element.animate(transformArray, { duration: 1000, fill: "forwards", easing: "ease" }); // 更新元素ID循环 currentId++; if (currentId === 4) { currentId = 1; } };
关键说明
fill: "forwards"会让元素保持动画的最终状态,不需要在finish后调用cancel,除非你需要重置元素到初始状态。- 当必须调用
cancel()时,一定要捕获finishedPromise的reject,否则会阻塞后续Promise链的执行。
内容的提问来源于stack exchange,提问作者吴自强
相关产品推荐
相关产品推荐

