如何处理setTimeout()块中的异步/等待调用?

时间:2019-04-08 22:48:05

标签: javascript node.js asynchronous

我有一个进程以特定的时间间隔运行,比如说它每分钟10秒运行一次。这是一个递归函数,因此在每10秒钟结束时,它会自行调用。到目前为止,这是可行的。

在此计时器函数中,我有一个对API的异步/等待调用。基本上,它的工作是进行API调用,获取数据,并将数据完成%100后将其推送到客户端。

问题是,如果async / await调用花费的时间超过了我的计时器间隔,那么我将开始出现重叠。

因此,这是一个设计问题。我通过在API调用包装函数中使用全局变量找到了解决方案。

var REQUEST_CYCLE_ACTIVE = false;

var REQUEST_CYCLE_ACTIVE = true;

在计时器内部,如果API调用未完成,我可以跳过递归,它将在下一个间隔尝试。

这有效。但是我很想知道是否有更优雅的方法来解决这个问题。我还提供了一个示例node.js应用程序,用于演示问题和解决方案。

// A simple function to simulate resource delays.
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// Construct a time string for debug purposes.
const getTime = () => {
    var now = new Date();
    var h = now.getHours().toString().padStart(2, '0');
    var m = now.getMinutes().toString().padStart(2, '0');
    var s = now.getSeconds().toString().padStart(2, '0');
    const timeSignature = h + ':' + m + ':' + s;
    return timeSignature;
}

// Update
// This is the request wrapper.
const update = async () => {
  REQUEST_CYCLE_ACTIVE = true;
  
  let result;
  try {
    result = await someFakeAPI();
  } catch (err) {
    throw err;
  }
  let timeSignature = getTime();
  console.log(`\t\t(REQUEST) update() is done. Here is the result:`);
  console.log('\t\t', result)
  
  // Once the data is %100 collected here (there are retries etc.)
  // we make a call to the websocket and force-push the data to all
  // of the connected clients.
  
  // PUSH TO CLIENTS()
  
  REQUEST_CYCLE_ACTIVE = false;
};

// The core mock API call function.
async function someFakeAPI() {
  // Emulate an asynchroneous fetch.
  console.log('\t\t(REQUEST) Fetching data ...')
  // 12000 miliseconds (12 sec) is longer than our timer call.
  // There will be an overlap.
  await delay(12000);
  let result = 0.6; // Change to 0.4 to trigger a failed fetch.
  if (result < 0.5) {;
    return '__FAIL__';
  } else {
    return {name: 'apple', price: '1234.12', time: 1549926859970};
  }
}

// Just an initial API call to get things started.
async function sendExchangeRequest()
{
  let result;
  try {
    result = await someFakeAPI();
  } catch (err) {
    throw err;
  }
  return result;
}

// runClock {{{1
const runClock2 = async () => {
  let next_update_seconds;
  // This is our interval, the recursive function will be called on every n'th second of the minute.
  // For example, an interval of 10 means, the calls will be placed at 22:11:00, 22:11:10, 22:11:20 etc.
  var interval = 10; //seconds
  var date = new Date();

  let current_seconds = date.getSeconds();

  let buffer = 60 % interval;
  let border = (60 - buffer);

  // Interval calculations.
  if (current_seconds <= border) {
    if ((current_seconds % interval) == 0) {
      next_update_seconds = interval;
    } else {
      let distance = (border - current_seconds);
      let slice = (distance % interval);
      next_update_seconds = slice;
    }
  } else {
    next_update_seconds = (60 - current_seconds);
  }
  
  let next_update_milliseconds = (next_update_seconds * 1000); 
  var timeToNextTick = (next_update_milliseconds);

  let timeSignature = getTime();
  console.log(`[4] <${timeSignature}> Starting runClock(), time to next Async REQUEST: ${timeToNextTick}ms`);

  setTimeout(function() {
    let timeSignature = getTime();
    console.log(`\t(${timeSignature}) Time is up, done waiting. Async REQUEST will be called.`);
    if(REQUEST_CYCLE_ACTIVE){
      console.log(`\t(${timeSignature}) Previous Async REQUEST already running. Skipping ...`);
    }else{
      console.log(`\t(${timeSignature}) Previous Async REQUEST is complete. Running a new one ...`);
      // MAKE THE ASYNC API CALL HERE.
      update();
    }
    // Recursion
    console.log(`\t(${timeSignature}) Start runClock() again.`);
    runClock2();
  }, timeToNextTick);
};

var REQUEST_CYCLE_ACTIVE = false;

(async () => {
  console.log('[1] Make the request:')
  try {
    response = await sendExchangeRequest();
    console.log('[2] Report the result:\n\t', response)
    console.log('[3] Start the clock cycle.');
    runClock2();
  } catch(error) {
    console.log('FAILED:', error)
  }
})();

如果您对所包含的代码有疑问,请参阅以下REPL链接:AsyncConcepts2.repl.run

最后,展示了该示例的gif: enter image description here

0 个答案:

没有答案