Порядок выполнения обработчиков

В библиотеке Page.js маршруты управляются с помощью цепочек функций-обработчиков, которые вызываются при совпадении URL. Каждый маршрут может содержать несколько обработчиков, выполняющихся последовательно. Понимание порядка их выполнения критично для построения корректной логики приложения.

Регистрация обработчиков

Обработчики регистрируются с помощью метода page(path, handler1, handler2, ...). Порядок передачи функций определяет порядок их вызова. Пример:

page('/dashboard', checkAuth, loadData, renderDashboard);

В этом примере:

  1. checkAuth проверяет авторизацию пользователя.
  2. loadData загружает необходимые данные.
  3. renderDashboard отвечает за отображение страницы.

Важно: каждый последующий обработчик вызывается только после вызова функции next() предыдущим обработчиком.

Использование функции next()

Функция next() передаётся в каждый обработчик автоматически. Если она не вызывается, выполнение цепочки останавливается.

function checkAuth(ctx, next) {
    if (!ctx.user) {
        page.redirect('/login');
    } else {
        next();
    }
}

В примере выше, если пользователь не авторизован, next() не вызывается, и последующие обработчики (loadData, renderDashboard) не выполняются.

Порядок глобальных и локальных обработчиков

Page.js поддерживает глобальные middleware, которые выполняются для всех маршрутов:

page('*', globalLogger);

Глобальные обработчики всегда вызываются перед обработчиками конкретного маршрута.

Порядок вызова:

  1. Глобальные middleware, зарегистрированные до маршрута.
  2. Обработчики конкретного маршрута, по порядку передачи в page().
  3. Глобальные middleware, зарегистрированные после маршрута, не вызываются для предыдущих маршрутов.

Асинхронные обработчики

Обработчики могут выполнять асинхронные операции. Для корректного порядка важно вызвать next() после завершения асинхронного кода:

function loadData(ctx, next) {
    fetch('/api/data')
        .then(response => response.json())
        .then(data => {
            ctx.data = data;
            next();
        });
}

Если next() вызвать до завершения загрузки данных, последующий обработчик получит пустой контекст.

Несколько маршрутов и перекрытие

Если несколько маршрутов совпадают с одним URL, Page.js выполняет первый зарегистрированный маршрут, полностью проходя его обработчики, и останавливается, если внутри обработчиков не используется next().

page('/profile', auth, renderProfile);
page('/profile/settings', auth, renderSettings);

При переходе на /profile/settings выполняются обработчики маршрута /profile/settings. Обработчики /profile не выполняются, так как URL отличается, даже несмотря на частичное совпадение.

Прерывание цепочки

Можно намеренно прервать выполнение обработчиков, например, при ошибке или при редиректе:

function checkAdmin(ctx, next) {
    if (!ctx.user.isAdmin) {
        page.redirect('/not-authorized');
        return; // Прерываем цепочку
    }
    next();
}

Без return выполнение могло бы продолжиться и вызвать нежелательные эффекты.

Комбинирование маршрутов и middleware

Для сложных приложений часто создают цепочки middleware для разных уровней маршрутов:

page('/app', auth, layout);
page('/app/dashboard', loadDashboard, renderDashboard);
page('/app/settings', loadSettings, renderSettings);
  • auth выполняется для всех маршрутов /app*.
  • layout отвечает за общий каркас приложения.
  • Специфичные обработчики загружают данные и рендерят контент.

Такой подход позволяет структурировать обработчики по уровням ответственности.

Контекст и передача данных

Контекст (ctx) передаётся всем обработчикам маршрута и глобальным middleware. Можно хранить данные для последующих обработчиков:

function auth(ctx, next) {
    ctx.user = getCurrentUser();
    next();
}

function loadDashboard(ctx, next) {
    ctx.dashboardData = fetchDashboard(ctx.user.id);
    next();
}

function renderDashboard(ctx) {
    renderTemplate('dashboard', ctx.dashboardData);
}

Использование контекста обеспечивает последовательное накопление данных без глобальных переменных.

Итоговое правило

Порядок выполнения обработчиков в Page.js определяется:

  1. Порядком регистрации глобальных middleware (page('*', handler)).
  2. Порядком передачи функций в конкретный маршрут (page('/path', handler1, handler2)).
  3. Явными вызовами next() внутри обработчиков.
  4. Асинхронностью и корректной передачей данных через ctx.

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