Search code examples
javascriptevent-handling

Handling Promise.race better


Trying to write the flow logic for a drawing tool where user can enter a combination of keyboard inputs to draw complicated shapes on a canvas, to achieve this I'm trying to chain event listeners in a way that they remove themselves once done keeping global state clean. I would rather do this than keeping track hundreds of global state variabls that manages which listeners are active etc. I'm making progress but I'm stuck on recusive calls to Promise.race not sure if there's a better way to achieve this.

Using Promise.race in a recusive loop means that alot of the promises do not get resolved. Is there a better way for me to do this?

function startCount(node) {
  const counter = Object.create(counterMethods);
  counter.node = node;
  counter.number = 0;
  counter.exitloopflag = false;
  return counter;
}

const counterMethods = {
  doCounter: function () {
    return new Promise((resolve) => {
      let handler = () => {
        this.node.removeEventListener("click", handler);
        console.log("lip");
        resolve("click");
      };
      this.node.addEventListener("click", handler);
    });
  },
  quit: function () {
    return new Promise((resolve) => {
      let handler = (e) => {
        if (e.key === "Escape") {
          this.node.removeEventListener("keydown", handler);
          console.log("lap");
          resolve("exit");
        }
      };
      this.node.addEventListener("keydown", handler);
    });
  },
  counterFlow: async function () {
    //if (this.exit) return;
    let result = await Promise.race([this.doCounter(), this.quit()]);

    if (result === "exit") {
      console.log(`You have pressed exit`);
      //  this.exit = true;
    } else if (result === "click") {
      this.number += 1;
      console.log(`You have clicked ${this.number} of times`);
      this.counterFlow();
    } else {
      console.log("code should not have run here");
    }
  },
};

Solution

  • Ok the answer to this was pretty simple it seems, you just need to add both eventlisteners at the same time and remove them as soon as one is called. It's not elegant but it works:

    export class StartCount {
      constructor() {
        this.number = 0;
        this.again = true;
        this.s = window.Storage;
      }
    
      addOne() {
        return new Promise((resolve) => {
          let handleClick = (e) => {
            this.number += 1;
            console.log("hello");
            console.log(`X:${e.clientX} Y:${e.clientY} counter:${this.number}`);
            document.removeEventListener("click", handleClick); //remove both here
            document.removeEventListener("keydown", handleKeydown);
            resolve(0);
          };
    
          let handleKeydown = (e) => {
            if (e.key === "Escape") {
              console.log("Escaped");
              document.removeEventListener("click", handleClick);
              document.removeEventListener("keydown", handleKeydown);
              resolve(1);
            }
          };
    
          document.addEventListener("click", handleClick); //<-just call both here
          document.addEventListener("keydown", handleKeydown); // and here
        });
      }
    
      async flowLogic() {
        if (await this.addOne()) return;
        if (await this.addOne()) return;
        if (await this.addOne()) return;
        console.log(`end reached${this.again ? " again" : " ?"}`);
      }
    
      async doStartCount() {
        await this.flowLogic();
        document.dispatchEvent(this.s.modeNormal);
      }
    }