Home > Net >  setInterval based on ajax result or promise state
setInterval based on ajax result or promise state

Time:09-28

I need to send an ajax request with an interval of 5s

const asyncF = async () => { await /* some ajax stuff */ }

asyncF(); // fire off an ajax request first
setInterval(() => { // start an interval
  asyncF();
}, 5 * 1000);

Normally the asyncF should succeed within 5s, so 5s between sucessive starts are normally ok and simple, but I want to make sure when next asyncF fires, the previous one has succeeded. If the previous one didn't succeed, then start a new interval whenever previous one succeeds

  • if asyncF takes less than 5s to succeed, all is good!
  • if asyncF takes more than 5s to succeed, I need to clear the interval and start a new interval whenever asyncF succeeds
  • if asyncF fails within 5s, I also need to immediately call it again and set a new interval

I came up with this naive idea which kind of solves the problem

let flag = true; // global variable flag
const asyncF = async () => {
 await /* some ajax stuff */
 flag = true;
}

try {
  asyncF();
  setTimer();
} catch {
  asyncF();
  setTimer();
}

function setTimer() {
  let timer = setInterval(() => {
    if (flag) {
      asyncF();
    } else {
      clearInterval(timer);
      timer = setTimer();
    }
  }, 5 * 1000);
  return timer;
}

However with this method, if asyncF takes more than 5s to succeed, the timer start counting before asyncF succeeds

Is there any mature and graceful solution to solve the problem?

CodePudding user response:

It is easier to do this not with a setInterval and have to work out when to cancel/restart it, but with a setTimeout.

The idea is that you set a timer for 5 seconds, and if the previous function has finished you recall the function. If the timeout has been reached while the function is executing you execute it immediately, and same goes for if an error occurs.

async function limitedFunction(){
  let isRunning = true;
  let timeoutReached = false;
  setTimeout( () => {
    timeoutReached = true;
    if(!isRunning)
      limitedFunction();
  },5000);
  
  try{
    await asyncF();
    isRunning = false;
    if(timeoutReached)
      limitedFunction();
  } 
  catch {
    limitedFunction();
  }  
}

In the example below I have mocked out your asyncF function whereby it randomly suceeds or fails (80% chance of success) and has a random delay which sometimes goes over 5 seconds. However this is no different from your ajax-based async function. It would work exactly the same way!

You should see that

  • When the function succeeds in <5s it waits 5s since the original start to fire again
  • When the function succeeds in >5s it starts again immediately
  • When the function fails it starts again immediately.

Hopefully this matches your requirements

async function asyncF(){
  
  const delay = (ms) => new Promise(resolve => setTimeout(resolve,ms))
  
  const rndFunc = Math.random();
  const rndTime = Math.floor((Math.random() * 7)   1);
  
  if(rndFunc < 0.8){
    console.log(`asyncF will succeed in ${rndTime}s`)
    await delay(rndTime*1000)
  }
  else{
    console.log(`asyncF will fail in ${rndTime}s`);
    await delay(rndTime*1000)
    throw "I failed"
  }  
}

async function limitedFunction(){
  let isRunning = true;
  let timeoutReached = false;
  setTimeout( () => {
    timeoutReached = true;
    if(!isRunning)
      limitedFunction();
  },5000);
  
  try{
    await asyncF();
    isRunning = false;
    if(timeoutReached)
      limitedFunction();
  } 
  catch {
    limitedFunction();
  }  
}

limitedFunction()

  • Related