resolving a promise with EventListener

You do not need to pass the promise to the event handler, you need to pass the resolve callback:

function EventListenerForPopUp(resolve) {
            this.removeEventListener("animationend", EventListenerForPopUp );
            resolve();
}   

// [...]

        return new Promise(function(resolve, reject) {
            this.Div.addEventListener("animationend", function() {
                EventListenerForPopUp.call(this, resolve);
            }, false);

This looks a bit ugly to me, maybe you can look at something like this:

var div = this.Div;
return new Promise(function (resolve) {
    div.addEventListener("animationend", function animationendListener() {
        div.removeEventListener("animationend", animationendListener);
        //call any handler you want here, if needed
        resolve();
    });
});

Alternatively, we can create a reusable utility function as a means of creating a promise from any DOM event:

const createPromiseFromDomEvent = (eventTarget, eventName, run?) =>
    new Promise((resolve, reject) => {
        const handleEvent = () => {
            eventTarget.removeEventListener(eventName, handleEvent);

            resolve();
        };

        eventTarget.addEventListener(eventName, handleEvent);

        try {
            if (run) run();
        catch (err) {
            reject(err);
        }
    });

Example usage (with an MSE source buffer):

await createPromiseFromDomEvent(
    sourceBuffer,
    'update',
    () => sourceBuffer.remove(3, 10)
);

Depending on the situation, the run parameter may be needed to provide custom code to trigger the async operation (as above), or omitted if we know the operation has already started.


Another option is to abstract an externally controlled promise as a reusable function -

function thread () {
  let resolve, reject
  const promise = new Promise((res, rej) => {
    resolve = res
    reject = rej
  })
  return [promise, resolve, reject]
}

function customPrompt(form) {
  const [prompt, resolve] = thread()
  form.yes.addEventListener("click", _ => resolve(true), {once: true})
  form.no.addEventListener("click", _ => resolve(false), {once: true})
  return prompt
}

customPrompt(document.forms.myform)
  .then(response => console.log("response:", response))
<form id="myform">
  <input type="button" name="yes" value="yes" />
  <input type="button" name="no" value="no" />
</form>

You can use async and await if you wanted -

async function main () {
  const response = await customPrompt(document.forms.myform)
  console.log("response:", response)
}

You could easily add a timeout by modifying customPrompt -

function customPrompt(form) {
  const [prompt, resolve, reject] = thread()
  form.yes.addEventListener("click", _ => resolve(true), {once: true})
  form.no.addEventListener("click", _ => resolve(false), {once: true})
  
  // timeout after 30 seconds
  setTimeout(reject, 30000, Error("no response"))
  return prompt
}

For other creative uses of thread, see this Q&A.

Tags:

Javascript