Javascript 如何使用Promise链接CSS动画?

Javascript 如何使用Promise链接CSS动画?,javascript,ecmascript-6,css-animations,es6-promise,Javascript,Ecmascript 6,Css Animations,Es6 Promise,我试图弄清楚Promise是如何工作的,并制作一个又一个CSS动画。但它们同时运行 您需要等待动画完成,这是一个工作代码 let move_boxes = () => { return new Promise( (resolve, reject) => { resolve ('boxes moved!') }) }; let move_box_one = () => { return new Promise( (resolve,

我试图弄清楚
Promise
是如何工作的,并制作一个又一个CSS动画。但它们同时运行


您需要等待动画完成,这是一个工作代码

    let move_boxes = () => {
    return new Promise( (resolve, reject) => {
        resolve ('boxes moved!')
    })
};
let move_box_one = () => {
    return new Promise( (resolve, reject) => {
        document.getElementById('div_two').style.animation = 'move 3s forwards';
        setTimeout(()=> resolve(), 3000);   
    })

}
let move_box_two = () => {
    return new Promise( (resolve, reject) => {
        document.getElementById('div_one').style.animation = 'move 3s forwards';
        setTimeout(()=> resolve(), 3000);
    })
}

move_boxes().then(() => {
    return move_box_one();
}).then(() => {
    return move_box_two();
});

工作小提琴:

与发布的答案相反,我不鼓励使用
window.setTimeout
,因为它不能保证计时器与动画结束事件同步,动画结束事件有时会卸载到GPU。如果您想更加确定,您应该收听,并在回调本身中解析它,即:

let move_box_one = () => {
  return new Promise((resolve, reject) => {
    const el = document.getElementById('div_one');
    const onAnimationEndCb = () => {
      el.removeEventListener('animationend', onAnimationEndCb);
      resolve();
    }

    el.addEventListener('animationend', onAnimationEndCb)
    el.style.animation = 'move 3s forwards';
  });
}
更好的是,由于您为这两个框编写了一些重复的逻辑,因此可以将所有这些抽象为一个返回承诺的通用函数:

// We can declare a generic helper method for one-time animationend listening
let onceAnimationEnd = (el, animation) => {
  return new Promise(resolve => {
    const onAnimationEndCb = () => {
      el.removeEventListener('animationend', onAnimationEndCb);
      resolve();
    }
    el.addEventListener('animationend', onAnimationEndCb)
    el.style.animation = animation;
  });
}

let move_box_one = async () => {
  const el = document.getElementById('div_one');
  await onceAnimationEnd(el, 'move 3s forwards');
}
let move_box_two = async () => {
  const el = document.getElementById('div_two');
  await onceAnimationEnd(el, 'move 3s forwards');
}
另外,您的
move_box
函数有点复杂。如果要异步运行单个框移动动画,请将其声明为异步方法,只需等待单个框移动函数调用,即:

let move_boxes = async () => {
  await move_box_one();
  await move_box_two();
}
move_boxes().then(() => console.log('boxes moved'));
请参见概念验证示例(或者您可以从原始示例中看到):

//我们可以为一次性animationend监听声明一个通用的helper方法
让onceAnimationEnd=(el,动画)=>{
返回新承诺(解决=>{
const onAnimationEndCb=()=>{
el.removeEventListener('animationend',onAnimationEndCb);
解决();
}
el.addEventListener('animationend',onAnimationEndCb)
el.style.animation=动画;
});
}
让我们移动\u box\u one=async()=>{
const el=document.getElementById('div_one');
等待onceAnimationEnd(el,“向前移动3秒”);
}
让move\u box\u two=async()=>{
const el=document.getElementById('div_two');
等待onceAnimationEnd(el,“向前移动3秒”);
}
让我们移动\u box=async()=>{
等待移动盒子();
等待移动框二();
}
move_box()。然后(()=>console.log('box moved')
#第一部分{
宽度:50px;
高度:50px;
边框:10px纯红;
}
#二组{
宽度:50px;
高度:50px;
边框:10px纯蓝色;
}
@关键帧移动{
100% {
转换:translateX(300px);
}
}
@关键帧向下{
100% {
变换:translateY(300px);
}
}

只有当您知道相应的动画事件已完成时,才需要解决承诺

您可以为此使用事件,这比使用具有预期持续时间的
setTimeout
更合适

还可以创建一个实用函数,用于为此创建承诺,然后再将其用于任何元素和动画描述:

const promiseAnimation=(元素,动画)=>newpromise(解析=>{
elem.style.animation=动画;
元素addEventListener(“动画结束”,解析);
});
const$=document.querySelector.bind(文档);
const moveBoxOne=()=>promiseAnimation($(“#div_one”),“向前移动3秒”);
const moveBoxTwo=()=>promiseAnimation($(“#div_two”),“向前移动3秒”);
承诺,决心
.然后(移动BoxOne)
。然后(()=>console.log(“一个已经移动”))
.然后(移动框2)
。然后(()=>console.log(“两个已经移动”)
div{显示:内联块;位置:相对;边框:1px实体;}
@关键帧移动{
从{左边距:0px;}
到{左边距:100px;}
}
div\u one

div_two
承诺并不意味着它将是异步的。因为解析是即时的,所以它们很可能同时运行。如果您想要延迟,您可以使用setTimeout在调用中创建延迟

const delay = (dl) => new Promise(r => {
    setTimeout(r, dl)
})

let move_box_one = () => {
    document.getElementById('div_two').style.animation = 'move 3s forwards'
    console.log('box one moved!') 

}
let move_box_two = () => {
    document.getElementById('div_one').style.animation = 'move 3s forwards'
    console.log('box two moved!')       
}

(async function() {
    move_box_one()
    await delay(200)
    move_box_two()
})()

您正在将承诺返回函数调用正确地链接在一起。但是函数会立即解决它们返回的承诺,而不必等待动画完成!承诺不会使任何事情变得不同步。它们用于管理其他API中的异步行为。
const delay = (dl) => new Promise(r => {
    setTimeout(r, dl)
})

let move_box_one = () => {
    document.getElementById('div_two').style.animation = 'move 3s forwards'
    console.log('box one moved!') 

}
let move_box_two = () => {
    document.getElementById('div_one').style.animation = 'move 3s forwards'
    console.log('box two moved!')       
}

(async function() {
    move_box_one()
    await delay(200)
    move_box_two()
})()