nick
nick

Reputation: 3229

Javascript cancel await for animation sequence?

I have a function like this:

const init = async () => {
    const els = [...]; //array of html elements

    els[0].classList.add('hidden');
    await sleep(200);
    els[1].classList.remove('hidden');
    await sleep(500);
    els[3].classList.remove('hidden');
    await sleep(4000);
    els[3].classList.add('hidden');
    els[2].classList.remove('hidden');
    els[1].classList.add('hidden');
    await sleep(800);
    els[3].classList.add('out');
    els[4].classList.remove('hidden');
}

As you can see there's a 4 second await in there. I want to, using an external function that comes from a click, to be able to skip that 4000ms delay.

const cancelAnimation = () => {
    // whatever
}

I thought of using a flag variable to change the number from 4000 to 500 for example, but if it already gotten into that sleep(4000) it doesn't matter cause the number won't change.

So, is there any way to cancel this out?

Btw, this is the code from the sleep function:

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

Upvotes: 1

Views: 112

Answers (2)

Dmitriy Mozgovoy
Dmitriy Mozgovoy

Reputation: 1597

A bit of magic (https://codesandbox.io/s/green-dream-u2yxk?file=/src/index.js) :)

import CPromise from "c-promise2";

const init = () => CPromise.from(function* () {
    let skip = false;
    let promise;

    this.on("signal", (type) => {
      if (type === "skip") {
        promise ? promise.cancel() : (skip = true);
      }
    });

    console.log("stage1");
    yield CPromise.delay(200);
    console.log("stage2");
    yield CPromise.delay(500);
    console.log("stage3");
    if (!skip) {
        yield (promise = CPromise.delay(4000)).cancelled();
    }
    console.log("stage4");
    yield CPromise.delay(800);
    console.log("stage5");
  });

const task = init();

console.log(task instanceof Promise); // true

setTimeout(() => {
  task.emitSignal("skip");
}, 800);

Upvotes: 0

Michael S. Molina
Michael S. Molina

Reputation: 732

You can make your promise cancelable:

const cancelableSleep = (ms) => {
  let timeout;

  return {
    promise: new Promise((resolve) => {
      timeout = setTimeout(resolve, ms);
    }),
    cancel() {
      clearTimeout(timeout);
    },
  };
};

const init = async () => {
  const cancelable = cancelableSleep(10000);

  //simulate click in 2 seconds
  setTimeout(() => cancelable.cancel(), 2000);

  console.log("sleeping");
  await cancelable.promise;
  console.log("awake");
};

init();

Upvotes: 1

Related Questions