Построить клиент аналитики, буферизующий события и отправляющий их пачками
Реализуйте AnalyticsClient, собирающий события и отправляющий их на сервер пачками. Сделайте queueEvent(event) (буферизует событие, проставляя timestamp: Date.now()), периодическую отправку каждые interval мс, которая POST-ит все буферизованные события одним JSON-массивом, и sendNow() (отправить немедленно). Очищать буфер лишь после успешной отправки, чтобы неудачный сброс сохранил события для повтора.
class AnalyticsClient {
constructor({ url, interval }) {
// ваш код здесь
}
}
Допишите реализацию.
Держать массив events; queueEvent добавляет { ...event, timestamp: Date.now() }. Самоперепланируемый setTimeout зовёт sendNow, который выходит при пустом буфере, POST-ит события одним JSON-массивом и очищает буфер ТОЛЬКО при успехе — поэтому отклонённый fetch оставляет события в очереди для повтора. sendNow также вызывается напрямую для немедленного сброса.
- ✗Очищать буфер до успешной отправки, из-за чего неудачный POST молча теряет события
- ✗Отправлять поэлементно вместо пачки, что обесценивает буфер
- ✗Использовать
setInterval, позволяя медленному сбросу перекрыть следующий
- →Почему буфер надо очищать лишь после успешной отправки, а не до?
- →Как пакетирование событий снижает нагрузку по сравнению с немедленной отправкой каждого?
Решение
События копятся в массиве; сброс POST-ит пачку и очищает буфер лишь при успехе.
class AnalyticsClient {
#events = [];
#url;
#interval;
#timer = null;
constructor({ url, interval }) {
this.#url = url;
this.#interval = interval;
this.#schedule();
}
#schedule() {
this.#timer = setTimeout(() => {
this.sendNow().finally(() => this.#schedule());
}, this.#interval);
}
queueEvent(event) {
this.#events.push({ ...event, timestamp: Date.now() });
}
async sendNow() {
if (this.#events.length === 0) return;
const batch = this.#events;
await fetch(this.#url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ events: batch }),
});
this.#events = []; // очищаем только после успешного await
}
}
Как это работает
queueEvent лишь складывает событие в массив #events, проставляя метку времени. Реальная отправка происходит пачкой: sendNow собирает все накопленные события в одно JSON-тело и шлёт одним POST — это резко меньше запросов, чем слать каждое событие отдельно.
Ключевая деталь надёжности — буфер очищается строго после успешного await fetch. Если запрос упадёт, исключение прервёт sendNow до строки очистки, и события останутся в очереди для следующей попытки. Перепланирование в .finally гарантирует, что новый сброс не стартует, пока не завершился предыдущий.