Как обрабатывать асинхронные / ожидающие вызовы внутри блока setTimeout ()? - PullRequest
0 голосов
/ 09 апреля 2019

У меня есть процесс, который запускается через определенный интервал, скажем, он запускается каждую 10-ю секунду минуты. Это рекурсивная функция, поэтому в конце каждых 10 секунд она будет вызываться сама. Пока это работает.

Внутри этой функции таймера у меня есть асинхронный / ожидающий вызов API. По сути, он выполняет API-вызов, получает данные, и эти данные передаются клиенту после завершения% 100.

Проблема в том, что если этот асинхронный / ожидающий вызов занимает больше времени, чем интервал моего таймера, я начинаю получать перекрытия.

Итак, это немного вопрос дизайна. Я нашел решение, используя глобальную переменную внутри моей функции-оболочки вызова 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...