Возвращаемые значения

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


Отсутствие прямого использования возвращаемого значения

Функции-обработчики маршрутов в Page.js не используют возвращаемые значения в традиционном смысле. Любое значение, возвращаемое из обработчика, игнорируется библиотекой.

page('/home', () => {
  return 'Hello';
});

В данном примере строка 'Hello' никак не влияет на поведение маршрута.

Ключевая идея: управление осуществляется через побочные эффекты, а не через возвращаемые значения.


Контекст ctx как основной канал передачи данных

Вместо возврата значений используется объект контекста (ctx), который передаётся во все обработчики маршрута.

page('/user/:id', (ctx, next) => {
  ctx.userId = ctx.params.id;
  next();
}, (ctx) => {
  console.log(ctx.userId);
});

Особенности:

  • ctx сохраняет состояние между middleware
  • любые данные записываются как свойства объекта
  • доступен на всех этапах обработки маршрута

Таким образом, ctx выполняет роль “контейнера” вместо возвращаемых значений.


Управление потоком через next()

Функция next() определяет переход к следующему middleware. Это важнее, чем возврат значения.

page('/example', (ctx, next) => {
  if (!ctx.loggedIn) return;
  next();
}, () => {
  console.log('Авторизованный пользователь');
});

Здесь:

  • отсутствие вызова next() прерывает цепочку
  • return используется только для выхода из функции, а не передачи значения

Асинхронные операции и возвращаемые значения

При использовании async/await возвращаемое значение также не имеет значения для Page.js.

page('/data', async (ctx, next) => {
  const data = await fetch('/api/data').then(r => r.json());
  ctx.data = data;
  next();
});

Даже если функция возвращает значение:

return data;

оно не будет использовано.


Ошибки как альтернатива возврату

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

page('/secure', (ctx, next) => {
  if (!ctx.user) {
    throw new Error('Unauthorized');
  }
  next();
});

Либо используется специальный маршрут обработки ошибок:

page('*', (ctx) => {
  console.error('Ошибка маршрута');
});

Возвращаемые значения здесь не участвуют — управление идёт через исключения или изменение маршрута.


Прерывание выполнения

Возвращаемое значение может использоваться только для локального контроля выполнения функции:

page('/test', (ctx, next) => {
  if (ctx.skip) return;
  next();
});

Это не влияет на Page.js напрямую, а лишь завершает выполнение текущего обработчика.


Сравнение с другими подходами

Подход Возвращаемое значение Основной механизм
Обычные функции Используется return
Express middleware Не используется next()
Page.js Игнорируется ctx + next()

Использование цепочек middleware

В цепочках обработчиков возврат значений полностью заменён передачей данных через ctx:

page('/chain',
  (ctx, next) => {
    ctx.step1 = true;
    next();
  },
  (ctx, next) => {
    ctx.step2 = ctx.step1;
    next();
  },
  (ctx) => {
    console.log(ctx.step2); // true
  }
);

Каждый этап:

  • изменяет ctx
  • не возвращает значения
  • передаёт управление дальше

Работа с редиректами

Редиректы также не используют возвращаемые значения:

page('/old', () => {
  page.redirect('/new');
});

Даже если вернуть значение после редиректа, оно не будет обработано.


Частые ошибки

1. Ожидание возврата данных

const result = page('/route', () => 'data');

result будет undefined.

2. Попытка использовать return для передачи состояния

return { user: 'admin' };

Такая конструкция не имеет смысла в контексте Page.js.

3. Пропуск next()

page('/broken', (ctx, next) => {
  ctx.value = 1;
  // next() не вызван
});

Цепочка остановится, даже если есть следующий обработчик.


Рекомендации по работе

  • использовать ctx как единственный способ передачи данных
  • применять next() для управления порядком выполнения
  • избегать логики, завязанной на return
  • использовать return только для досрочного выхода из функции

Паттерн “сбор данных”

Один из распространённых подходов — последовательное накопление данных:

page('/profile',
  async (ctx, next) => {
    ctx.user = await getUser();
    next();
  },
  async (ctx, next) => {
    ctx.posts = await getPosts(ctx.user.id);
    next();
  },
  (ctx) => {
    renderProfile(ctx.user, ctx.posts);
  }
);

Каждый middleware:

  • добавляет данные в ctx
  • не возвращает результат
  • передаёт управление дальше

Итоговая модель

В Page.js отсутствует концепция значимых возвращаемых значений из обработчиков маршрутов. Архитектура строится на трёх принципах:

  • Контекст (ctx) — хранение и передача данных
  • Последовательность (next()) — управление потоком
  • Побочные эффекты — выполнение логики (рендеринг, редиректы, запросы)

Такой подход делает маршрутизацию гибкой и предсказуемой, особенно в сложных одностраничных приложениях.