Search code examples
javascriptes6-promise

Wait a maximum of N seconds for something to happen


In my JS code I needed to wait up to 3 seconds for a flag to be true. I did the following:

const waitForSomethingToBeTrue = (something) => {
    const maxWaitingTime = 3 * 1000;

    let promises = [];
    for (let i = 100; i < maxWaitingTime; i += 100) {
        promises.push(new Promise((resolve, reject) => {
            setTimeout(() => {
                if (something()) {
                    resolve();
                } else {
                    reject(something.name + " didn't happen in " + i + "miliseconds.");
                }
            }, i);
        }));
    }

    return Promise.any(promises).catch(() => {
        return Promise.reject(`${something.name} didn't happen in maximum allowed
            time (${maxWaitingTime} msecs).`);
    });
};

As you can notice, there are a couple of problems with my approach:

  1. I'm creating many unnecessary timeouts, for example, if promise at the 200msec mark is resolved, rest of them is unneeded and will already resolve
  2. I'm using Promise.any, which is not officially supported yet. I solved this by writing a shim, using Promise.all reversed, but still, an officially supported method would be better.

how can I solve this problem without having the above issues?


Solution

  • I would do this with just a single promise, which either resolve if something is true or reject if it gets to the time

    const waitForSomethingToBeTrue = (something) => {
      return new Promise( (resolve,reject) => {
        let start = new Date().getTime();
        const intervalId = setInterval( () => {
          if(something()){
            clearInterval(intervalId);
            resolve();
          }
          else{
            timer = (new Date().getTime() - start);
            if( timer > 3000){
              clearInterval(intervalId);
              reject("took too long");
            }
          }
        },100);
      });
    }
    
    // test code below just sets a global bool to true by clicking the button
    var globalBool = false;
    async function testIt(){
      document.querySelector("#clickme").addEventListener("click",() => globalBool = true);
      
      try{
        await waitForSomethingToBeTrue(() => globalBool);
        console.log("It was true");
       }
       catch(error){
        console.log(error);
       }
    }
    
    testIt();
    <button id="clickme">Click me to make something true</button>