animation.finished 不触发通常因动画被取消或未启动;需用.catch捕获cancel、确保元素已渲染、立即链式调用并保留引用,避免gc导致promise失效。

animation.finished 为什么有时不触发
直接监听 animation.finished Promise 却没进 .then(),大概率是因为动画被中途取消(比如元素被移除、animation.cancel() 调用)或动画根本没真正启动(例如元素未渲染、display: none、CSS 动画名拼错、animation-duration: 0s)。Promise 只在动画自然结束或显式调用 finish() 时 resolve;被 cancel 会 reject,但默认不抛错,容易误以为“没反应”。
实操建议:
- 务必用
.catch(() => {})捕获 cancel 场景,避免静默失败 - 检查元素是否已挂载到 DOM,可用
getComputedStyle(el).animationName确认 CSS 动画是否生效 - 避免在
display: none或visibility: hidden元素上调用animate(),它们会导致动画不启动
正确链式调用 animation.finished
必须在调用 Element.animate() 后立刻链上 finished,不能等几帧再取 —— 因为返回的 Animation 实例一旦被 GC 或重用,finished 就失效了。
示例(安全写法):
const anim = el.animate(keyframes, options);
anim.finished.then(() => {
console.log('动画完成');
// 后续逻辑:如移除元素、切换状态、触发事件
}).catch(() => {
console.log('动画被取消或中断');
});
注意:anim 必须保留引用(哪怕只用于 .finished),否则 V8 可能提前回收,导致 Promise 永不 resolve/reject。
与 CSS 动画 class 切换混用时的坑
如果用 el.classList.add('animate-slide') 触发 CSS 动画,el.getAnimations() 返回的 Animation 实例才是能访问 finished 的对象 —— 不是靠 class 名去“猜”。而且一个元素可能有多个动画,需过滤出目标动画。
实操建议:
- 用
el.getAnimations().find(a => a.effect?.getTarget() === el)定位当前元素的动画 - 若同时存在 transition 和 animation,
getAnimations()默认只返回Animation(即 CSS @keyframes 或 JS animate() 创建的),不包括 CSS transition - 不要依赖
animationend事件和finished同时使用 —— 二者触发时机基本一致,但finished更可靠(支持 cancel/reject,且可 await)
在 async 函数中 await animation.finished
可以 await,但要注意:await 的是 Promise,不是 Animation 实例本身。且 await 不会自动捕获 cancel,仍需 try/catch。
示例:
async function runSequence() {
const anim = el.animate(keyframes, { duration: 300 });
try {
await anim.finished;
console.log('OK,继续下一步');
} catch {
console.log('动画没跑完就被干掉了');
}
}
关键点:await 之后的代码,只会在动画自然结束时执行;cancel 或元素销毁都会走到 catch。如果你需要“无论如何都执行后续”,就得把逻辑放到 finally 里 —— 但要小心,finally 里无法区分是成功还是失败。
真正容易被忽略的是:动画实例的生命周期和 DOM 存活强绑定。元素被 remove() 后,其关联的所有动画会立即 cancel,finished reject,但这个过程不是异步 microtask,而是同步发生 —— 所以如果在 remove 前没设好 catch,就真的 silent fail 了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











