Background Sync — механизм, позволяющий выполнять асинхронные операции на фоне, не блокируя основной поток интерфейса, с автоматическим обновлением представлений в момент завершения операции. В контексте Mithril.js это особенно полезно для управления состоянием приложения и работы с удалёнными API, сохраняя реактивность компонентов.
Mithril использует виртуальный DOM и односторонний поток данных.
Любые изменения модели требуют явного вызова m.redraw(),
если они происходят вне цикла событий Mithril. Background Sync
позволяет:
m.requestОсновным инструментом для работы с удалёнными данными является
m.request. Он возвращает Promise, что идеально
подходит для Background Sync:
let user = null;
function loadUser() {
return m.request({
method: "GET",
url: "/api/user"
}).then(result => {
user = result;
});
}
Здесь loadUser запускает запрос, а по завершении
изменяет модель. Для автоматической перерисовки после изменения
состояния можно обернуть её вызовом m.redraw():
function loadUser() {
return m.request({
method: "GET",
url: "/api/user"
}).then(result => {
user = result;
m.redraw();
});
}
oninit и oncreate для Background SyncВ компонентах Mithril асинхронные задачи часто запускаются в хуках жизненного цикла:
const UserProfile = {
oninit: vnode => {
vnode.state.loading = true;
m.request({ method: "GET", url: "/api/user" })
.then(result => {
vnode.state.user = result;
})
.finally(() => {
vnode.state.loading = false;
m.redraw();
});
},
view: vnode => {
if (vnode.state.loading) return m("p", "Загрузка...");
return m("div", [
m("h2", vnode.state.user.name),
m("p", vnode.state.user.email)
]);
}
};
m.streamДля сложных сценариев Background Sync удобно использовать реактивные
потоки m.stream:
const userStream = m.stream();
function fetchUser() {
m.request({ method: "GET", url: "/api/user" }).then(userStream);
}
const UserProfileStream = {
oninit: fetchUser,
view: () => m("div", [
userStream() ? m("p", userStream().name) : m("p", "Загрузка...")
])
};
m.redraw().Асинхронные операции могут завершаться с ошибкой. В контексте Background Sync это важно для корректного отображения состояния интерфейса:
const state = { user: null, error: null, loading: false };
function loadUser() {
state.loading = true;
m.request({ method: "GET", url: "/api/user" })
.then(result => state.user = result)
.catch(err => state.error = err)
.finally(() => {
state.loading = false;
m.redraw();
});
}
Background Sync в Mithril поддерживает параллельные асинхронные запросы:
function loadData() {
state.loading = true;
Promise.all([
m.request({ url: "/api/users" }),
m.request({ url: "/api/posts" })
]).then(([users, posts]) => {
state.users = users;
state.posts = posts;
}).finally(() => {
state.loading = false;
m.redraw();
});
}
Promise.all объединяет несколько запросов, обеспечивая
синхронное обновление состояния после завершения всех операций.Чтобы избежать лишних перерисовок при Background Sync, можно
использовать опцию m.redraw.strategy("diff") или
контролировать вызовы m.redraw() вручную. Например:
let timer = setInterval(() => {
m.request({ url: "/api/updates" }).then(data => {
state.updates = data;
m.redraw(); // вызывается только при изменении данных
});
}, 5000);
Background Sync удобно комбинировать с IndexedDB или localStorage для кэширования:
function loadCachedUser() {
const cached = localStorage.getItem("user");
if (cached) state.user = JSON.parse(cached);
m.request({ url: "/api/user" })
.then(result => {
state.user = result;
localStorage.setItem("user", JSON.stringify(result));
})
.finally(() => m.redraw());
}
m.request и потоками
m.stream.Background Sync в Mithril обеспечивает устойчивую и отзывчивую архитектуру, позволяя строить сложные асинхронные интерфейсы с минимальными усилиями по управлению состоянием и перерисовкой компонентов.