基于 ajax 结果或承诺状态的 setInterval

setInterval based on ajax result or promise state

我需要发送一个ajax请求,间隔5s

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

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

通常 asyncF 应该在 5 秒内成功,因此连续启动之间的 5 秒通常是可以的并且很简单,但我想确保下一个 asyncF 触发时,前一个已经成功。如果前一个没有成功,那么每当前一个成功时开始一个新的间隔

我想出了这个解决问题的天真的想法

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;
}

但是使用这种方法,如果 asyncF 需要超过 5 秒才能成功,计时器会在 asyncF 成功之前 开始计时

有没有什么成熟优雅的方案可以解决问题?

不使用 setInterval 更容易做到这一点,并且必须计算出何时 cancel/restart 它,但是使用 setTimeout.

这个想法是你设置一个 5 秒的计时器,如果之前的功能已经完成,你就可以调用该功能。如果在函数执行时达到超时,则立即执行它,如果发生错误也是如此。

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();
  }  
}

在下面的示例中,我模拟了您的 asyncF 函数,它随机地成功或失败(80% 的成功机会)并且有一个随机延迟,有时会超过 5 秒。然而,这与基于 ajax 的异步函数没有什么不同。它的工作方式完全相同!

你应该看到

  • 当函数在 <5 秒内成功时,它会等待 5 秒,因为原始函数再次开始触发
  • 当函数在 >5 秒内成功时,它会立即再次启动
  • 当函数失败时,它会立即重新启动。

希望这符合您的要求

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()