Node.js (или другие асинхронные фреймворки): А как отлавливать ошибки?

Вот такой вопрос. Может быть мой задеревенелый синхронный мозг просто не видит решения, которое лежит на поверхности. Но я не понимаю, как обрабатывать ошибки при асинхронном подходе, как выдавать пользователю 500-страницу. Ведь как устроен привычный синхронный запрос (схематично):
try {
  responce = (function request(req) {
    var user = auth.getUser(req);
    if (user.auth) {
      var data = db.getData();
      if (data) {
        return new Responce(template.render(data));
      }
    }
    return error404;
  })(req);
} catch(e) {
  show500(e);
}


Все очень надежно, контроллер обязан возвратить ответ, любые ошибки, не отловленные внутри request, отлавливаются снаружи и выдают пользователю красивую страничку.

Что же при асинхронном подходе node.js?
try {
  (function request(req, res) {
    auth.getUser(req, function(user) {
      if ( ! user.auth) {
        return res.error404();
      }
      db.getData(function(data) {
        if ( ! data) {
          return res.error404();
        }
        res.write(template.render(data));
      });
    });
  })(req, res);
} catch(e) {
  res.show500(e);
}


И вот представте, что где-то в user.auth происходит исключение. Что произойдет? А ничего, function(user) прекратит выполнение, ответ обработать некому, соединение с браузером останется висеть до таймаута. Даже поставив где-то глобальный обработчик ошибок, не удастся вернуть пользователю ответ, потому что в самом объекте исключения нет доступа к объектам запроса и ответа.

Неужели, единственный выход ловить исключения вручную на каждом асинхронном вызове?
try {
  (function request(req, res) {
    auth.getUser(req, function(user) {
      try {
        if ( ! user.auth) {
          return res.error404();
        }
        db.getData(function(data) {
          try {
            if ( ! data) {
              return res.error404();
            }
            res.write(template.render(data));
          } catch(e) {
            res.show500(e);
          }
        });
      } catch(e) {
        res.show500(e);
      }
    });
  })(req, res);
} catch(e) {
  res.show500(e);
}
  • Вопрос задан
  • 3722 просмотра
Пригласить эксперта
Ответы на вопрос 6
К node.js имею косвенное отношение, но мне всегда казалось, что:
  • всеми силами надо избегать выброса исключений, а возвращать статус ошибки
  • вызов ассинxорнной функции должен иметь вид:
    asyncFunction(args, function(error, result){})
И теперь, если перевести эти пункты на ваш код:
auth.getUser(req, function(error,user) {
  if (error != null){
    handle(res,error);
    return;
  }
  db.getData(function(error,data) {
    if (error != null){
      handle(res,error);
      return;
    }
  res.write(template.render(data));
});

Проверки исключений здесь, мне кажется, излишни. Слабое место только template.render(data), надо просто проверить или эта функция бросает исключения. И даже если да, то лучше проверить или объект data валидный перед передачей в render. Пока сам придерживаюсь правила, если в функции для ее нормальной работы надо больше 2/3 if-ов, тогда только try..catch. К сожалению, в Яваскрипте нельзя так гибко обращаться с исключениями как в .NET(печальный вздох). Буду тоже рад если, кто то предложит более конструктивный ответ.
Ответ написан
akzhan
@akzhan
Посмотрите на thechangelog.com/post/516202796/step-control-flow-the-node-js-way

Как раз единообразит обработку ошибок и исключений.
Ответ написан
Комментировать
@zohkiller

Юзаем domain http://habrahabr.ru/post/147233/

Вообще специфика обработки на ноде это callback(error, data) где в колбек улетает ошибка

Ответ написан
Комментировать
@Jazzist
Firebag — мегапопулярный плагин для Mozilla Firefox, отлично решает как раз эту задачу:
— в консоли вы видете все входящие и исходящие запросы
— вы видите содержание этих запросов
— вы видите как изменяется HTML и CSS
— вы видите ошибки, в т.ч. сообщения об ошибках сервера
— …

Firebag устанавливается из меню Mozilla Firefox->Правка->Настройки->Дополнения->Настроить дополнения->Поиск дополнений
Ответ написан
@betula
Я советую посмотреть в сторону co, koa.
Ответ написан
Комментировать
@aleks_raiden
генерируйте везде евент — типа error_500, и прекращайте работу. а в обработчике евента уже формируете ответ юзеру и выводите. тогда в любом коде достаточно сгенерировать событие и будет юзеру нужная страничка
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы