-1

I have written a loop, which I want to run, every second, until the present time is equal to a time specified outside of the loop. If the time matches, it would log 'it is time', and break out of the loop; otherwise, it would run again in one second.

I've used node to run the file, and the file hangs on the execution, so I have to CTRL+C out, but it never prints anything.

This is clearly a bug on my end, but I can't figure out why it's happening.

  while (true)  { async ()=>{

    const now = new Date();
    const nowHour = now.getHours();
    const nowMinute = now.getMinutes();

    if (nowHour === hTarget && nowMinute === mTarget) {
      console.log('it is time!');
      return; // exit the loop when the target time is reached
    }
    console.log('not yet');
    // Wait for 1 second before checking again
    // to avoid excessive CPU usage
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
  }

I've tried running it without an async function, but I just run into stackoverlow errors.

  • You don't need a loop. Just await the promise that calls `setTimeout()`. See https://stackoverflow.com/questions/33289726/combination-of-async-function-await-settimeout – Barmar Feb 28 '23 at 17:22
  • Given execution is based on a certain system time, it's far more efficient to just queue the function for the appropriate time rather than polling every second, particularly when the time is set in minutes. If you're worried about slippage in the timeout queue, set the timer for 95% of the lag from now until the time. Then check if time's up and if not, set it for 95% of the lag again or if within say 10 seconds, just set the timeout to 10.1 seconds and run the function. – RobG Mar 01 '23 at 00:36

2 Answers2

0

The loop inside the anonymous function

save your function inside a var so you can call it let func = async() => {

Call your function func();

const hTarget = 14;//change to test
const mTarget = 35;//change to test

let func = async() => {
  while (true) {
    const now = new Date();
    const nowHour = now.getHours();
    const nowMinute = now.getMinutes();

    if (nowHour === hTarget && nowMinute === mTarget) {
      console.log('it is time!');
      return; // exit the loop when the target time is reached
    }
    console.log('not yet');
    // Wait for 1 second before checking again
    // to avoid excessive CPU usage
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
}

func();
Chris G
  • 1,598
  • 1
  • 6
  • 18
0

Maybe you're just playing with promises, but to me it seems way simpler to use a plain timeout. If you need to poll for some reason, set the timeout to close to the required time, then when close (say 5 or 10 seconds), just set it to the remaining lag.

// A function
function sayHi(){
  console.log('Hi ' + new Date().toTimeString().substring(0,8));
}

// Convert hr, min to milliseconds
function hmToMs(hr, min) {
  return hr*3.6e6 + min*6e4;
}

function runAt(hr, min, fn) {
  let sinceMidnight = Date.now() - new Date().setHours(0,0,0,0);
  let msLeft = hmToMs(hr, min) - sinceMidnight;
  // debug
  console.log(msLeft + ' ms left');
  
  // If more than 10 seconds left, schedule runAt again
  // when 95% of msLeft have elapsed
  if (msLeft > 1e4) {
    setTimeout(()=>runAt(hr, min, fn), msLeft * 0.95);
    
  // Otherwise, just schedule function to run at required time
  } else {
    setTimeout(fn, msLeft);
  }
}

// Run on next full minute
let d = new Date();
console.log(`Run at: ${d.getHours()} h ${d.getMinutes() + 1} min`);
runAt(d.getHours(), d.getMinutes() + 1, sayHi);

Setting it to run when 95% of the remaining time has elapsed avoids over polling and means the timer updates when close to the required time. Hopefully that avoids errors from very long wait times.

RobG
  • 142,382
  • 31
  • 172
  • 209