resle
resle

Reputation: 2364

DOM event callback within async function

Consider this function

async function Animate(element)
{
 // do something with dom element's animation, triggering it
  
 element.addEventListener("animationend", 
 function(event)
 {
  // this is when Animate(element) should resolve/return
 }

}

Is there any way to deal with this scenario, and actually have an async function resolve/return upon an event listener callback?

Upvotes: 0

Views: 1124

Answers (2)

Kaiido
Kaiido

Reputation: 136678

You could return or await a new Promise inside your async function and call its resolver in the event handler, but the thing with most events (and animationend is part of it) is that they may never fire, so you could be awaiting for something that will never happen.

In this particular case of an animationend, you can solve this issue thanks to the Web-Animations API which exposes a finished Promise that will either resolve or reject if the animation got stopped before its expected end:

const elem = document.querySelector(".animate");
(async () => {
  // never gonna resolve
  const prom = new Promise((resolve) => {
    elem.addEventListener("animationend", () => resolve(), { once: true });
  });
  // await prom;
  prom.then(()=>console.log("event fired"));

  // using the Web Animation API
  // we should do more filtering on the Animations here
  // to be sure we get our own
  const anim = elem.getAnimations()[0];
  try {
    await anim.finished;
  }
  catch(err) { }
  console.log("anim ended");
})().catch(console.error);
// stop the animation before completion
setTimeout(() => elem.remove(), 2000);
.animate {
  width: 50px;
  height: 50px;
  background: green;
  animation: anim 10s linear;
}
@keyframes anim {
  to {
    transform: translate(120px,0);
  }
}
<div class="animate"></div>

Upvotes: 1

epascarello
epascarello

Reputation: 207501

Use a promise. In the animationend event listener, resolve it.

const animateThis = async(elem) => {
  return new Promise(resolve => {
    elem.classList.add("active");
    elem.addEventListener('animationend', () => {
      elem.classList.remove("active");
      resolve();
    });
  });
};

(async function() {
  const elem = document.querySelector(".animation");
  console.log("before call");
  await animateThis(elem);
  console.log("after call");
}());
.animation.active {
  animation-duration: 2s;
  animation-name: slidein;
  animation-iteration-count: 2;
}

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }
  to {
    margin-left: 0%;
    width: 100%;
  }
}
<p class="animation">Hello World</p>

Upvotes: 1

Related Questions