Оптимистичные обновления (optimistic updates) — это техника,
позволяющая интерфейсу мгновенно реагировать на действия пользователя,
не дожидаясь ответа сервера. В контексте Mithril.js она реализуется
через прямое изменение состояния модели и последующий синхронный вызов
m.redraw(). Такой подход делает интерфейс более отзывчивым,
снижает задержки и улучшает восприятие приложения.
При использовании оптимистичных обновлений изменения сначала применяются локально, а затем отправляется асинхронный запрос на сервер. В случае успеха изменений достаточно подтвердить локальное состояние, при ошибке — откатить изменения до исходного состояния.
Пример схемы действий:
m.redraw() для мгновенного обновления
интерфейса.const tasks = [
{ id: 1, title: 'Выучить Mithril', done: false },
{ id: 2, title: 'Написать статью', done: false }
];
const toggleTask = (task) => {
// Сохраняем текущее состояние
const prevDone = task.done;
// Применяем оптимистичное обновление
task.done = !task.done;
m.redraw();
// Отправляем запрос на сервер
fetch(`/api/tasks/${task.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ done: task.done })
})
.then(response => {
if (!response.ok) {
// Откат при ошибке
task.done = prevDone;
m.redraw();
}
})
.catch(() => {
task.done = prevDone;
m.redraw();
});
};
const TaskList = {
view: () => m('ul', tasks.map(task =>
m('li', {
onclick: () => toggleTask(task),
style: { textDecoration: task.done ? 'line-through' : 'none', cursor: 'pointer' }
}, task.title)
))
};
m.mount(document.body, TaskList);
В этом примере клик по элементу списка мгновенно меняет его состояние визуально, не дожидаясь ответа сервера. Если сервер возвращает ошибку, состояние возвращается к прежнему.
При работе с оптимистичными обновлениями важно учитывать параллельные запросы. Один из подходов — использование очереди изменений:
let pendingUpdates = [];
const applyUpdate = (task, newState) => {
const prevState = { ...task };
Object.assign(task, newState);
m.redraw();
pendingUpdates.push({ task, prevState });
fetch(`/api/tasks/${task.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newState)
})
.then(response => {
if (!response.ok) throw new Error('Ошибка сервера');
pendingUpdates = pendingUpdates.filter(u => u.task !== task);
})
.catch(() => {
Object.assign(task, prevState);
m.redraw();
pendingUpdates = pendingUpdates.filter(u => u.task !== task);
});
};
Такой подход позволяет безопасно управлять множественными обновлениями, предотвращая неконсистентное состояние интерфейса.
Оптимистичные обновления особенно полезны для интерактивных списков, чекбоксов и форм, где каждое действие пользователя должно отражаться мгновенно. Для списков часто применяется подход предварительного добавления/удаления элемента:
const addTask = (title) => {
const newTask = { id: Date.now(), title, done: false };
tasks.push(newTask);
m.redraw();
fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTask)
})
.then(response => response.json())
.then(data => {
// Синхронизация id с сервером
newTask.id = data.id;
})
.catch(() => {
// Откат в случае ошибки
tasks = tasks.filter(t => t !== newTask);
m.redraw();
});
};
m.redraw() — ключевой инструмент для
оптимистичных обновлений. Он гарантирует немедленное обновление
интерфейса после изменения состояния.Оптимистичные обновления в Mithril.js создают ощущение мгновенной реакции интерфейса, делая приложения более динамичными и отзывчивыми, при этом требуют аккуратного управления состоянием и обработки ошибок для предотвращения рассогласования данных.