How to stop function from returning new result until x seconds is over, and return previous result if called too soon

Viewed 59

Creating a function onceEvery(), that allows a function to be called only once every x milliseconds. The first result is able to return, but the function doesn't stop it from being recalled before cooldown time is completed, returning the previous result instead. Thanks in advance!

onceEvery =  function (func, wait) {
//return function then can only return every * seconds
  let cooldown = false;
  let result;
  let previous;
  //if called again while cooldown = true, return previous result
  if (!cooldown) {
    result = func;
    previous = result;
    beginCoolDown();
    return result;
  } else {
    return previous;
  }
  
  function beginCoolDown () {
    cooldown  = true;
    setTimeout (function () {
      cooldown = false;
    }, wait);
   }
  }

3 Answers

You do this by keeping track of the last result you provided and when you last provided it, outside the function, and then using that when the function is called to decide whether to update that information:

const minInterval = 5000;       // 5 seconds (for example)
let lastResult = null;          // Last result provided
let nextAvailable = Date.now(); // When the next can be provided
function onceEvery() {
    const now = Date.now();
    if (now < nextAvailable) {
        return lastResult;
    }
    lastResult = /*...calculate result...*/;
    nextAvailable = now + minInterval;
    return lastResult;
}

Live Example:

const minInterval = 5000;       // 5 seconds (for example)
let lastResult = null;          // Last result provided
let nextAvailable = Date.now(); // When the next can be provided
function onceEvery() {
    const now = Date.now();
    if (now < nextAvailable) {
        console.log(Date.now(), "Too soon, returning last value:", lastResult);
        return lastResult;
    }
    lastResult = (lastResult || 0) + 1;
    nextAvailable = now + minInterval;
    console.log(Date.now(), "Calculated new value:", lastResult);
    return lastResult;
}

// Call it every second or so
setInterval(onceEvery, 1000);

You can use the concept of JavaScript closures. Return a wrapped function which executes only after waiting wait period.

To do this, maintain a flag isCalled on the outer function, which is initially false so that the function can be called immediately.

Once called the isCalled is set to true so that on next invocations the function has to wait until the timer executes and resets it to false:

function onceEvery(func, wait) {
  let isCalled = false;
  let result;
  return () => {
    //if not called call the func
    if (!isCalled) {
      result = func();
      //set to true
      isCalled = true;
      setTimeout(() => {
        //reset after waiting wait period
        isCalled = false;
      }, wait);
    }
    //return computed value
    //returns old value until new value is computed
    return result;
  }
}

let func = onceEvery(() => Date.now(), 2000);

setInterval(() => {
  console.log(func());
}, 400)

You have to define function beginCoolDown() outside of the onceEvery(), and move the definition of the cooldown outside of the function. That would be look like this:

let cooldown = false
function beginCoolDown() {
...
}
function onceEvery() {
...
}
Related