У меня есть процесс, который запускается через определенный интервал, скажем, он запускается каждую 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, демонстрирующий пример: