async ожидают (пробуют / ловят) в NodeJS - PullRequest
0 голосов
/ 24 сентября 2019

Я работаю над NodeJS API и мне было интересно, какой из следующих 2 примеров в ROUTES FILE является наилучшей практикой.Как вы можете видеть в одном я добавляю try / catch, а в другом я нет. Обратите внимание в файле Service.js . Я также добавил try / catch.Нужно ли это только в одном месте или лучше использовать его в обоих местах?

ФАЙЛ МАРШРУТОВ

 const todo = router => {
      /* EXAMPLPE 1 */
      router.get('/tasks', isAuth, async (req, res, next) => {
        const results = await TodoService.getTasks();
        return res.status(201).json(results);
      });

      /* EXAMPLPE 2 */
      router.get('/tasks', isAuth, async (req, res, next) => {
        try {
           const results = await TodoService.getTasks();
           return res.status(201).json(results);
         } catch (e) {
             return next(e);
         }
      };

SERVICE.JS

class TodoService {
  static async getTasks() {
    try {
      const tasks = await TaskModel.find();
      return {
        message: 'Fetched posts successfully.',
        tasks: tasks,
        status: 200
      };
    } catch (err) {
      if (!err.statusCode) {
        err.statusCode = 500;
      }
      return err;
    }
  }
}

Ответы [ 4 ]

2 голосов
/ 24 сентября 2019

В теперь отредактированной версии вашего вопроса, getTasks() НИКОГДА не может отклонить его обещание (так как вы ловите любые возможные отклонения и превращаете их в решенное обещание).Следовательно, вызывающая сторона в вашем примере 2 содержит абсолютно бессмысленный try/catch, который по сути является мертвым кодом, который никогда не может быть использован.Хотя это выглядит так, как будто это может быть более безопасный код, в основном это потраченный впустую код.

Укажите в документации / контракте для функции абсолютно ясно, может ли она быть отклонена или нет.Если это невозможно, то у вызывающей стороны нет причин пытаться обработать отклонение, поскольку это просто бессмысленный код.

Лично я думаю, что, возможно, более гибко позволять ошибкам возвращаться через отклоненное обещание, посколькуэто может использоваться в большем количестве мест и объединяться с другими операциями более легко.Но, если ЕДИНСТВЕННАЯ цель - это единственная часть обработчика запросов, тогда он может сохранить код, чтобы централизовать перехват брака и превратить его в разрешенный объект, который вы возвращаете таким, каким вы являетесь сейчас.Менее гибкий для различных типов вызывающих, но, возможно, более простой код для централизации отказов, если цель вызова функции всегда узка.

Нет правильного / неправильного ответа.В основном это вопрос того, что вы больше всего пытаетесь оптимизировать (гибкое использование вызывающего абонента или централизованный сбор ошибок) и ваших личных предпочтений в стиле.

1 голос
/ 24 сентября 2019

Таким образом, ответ зависит от стиля кода.У Javascript есть много способов структурировать код.Ваш первый вариант ближе к функциональному стилю, использующему функцию стрелки, второй использует настройку класса, представленную в ES2015.

Однако, только первый фактически создает маршрутизатор.Поэтому вам все еще нужен код для сопоставления ваших статических методов в TodoService с URL-адресами.

Разработчики, пришедшие с Java, обнаружат, что ваш код на основе классов будет легче следовать.Людям, имеющим опыт работы с небольшими сервисами в экспрессе, может быть легче следовать первому примеру.

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

Так что для меня ваш первый близок.У меня просто не было бы анонимных функций.Вместо этого эти функции должны быть во внешнем файле, возможно, routes.js, и настроены так, чтобы вы могли легко их тестировать.

например

rout.js

function makeTaskRoute = function(TodoService) {
 return async getTasks(req, res, next) => {
    const results = await TodoService.getTasks();
    return res.status(201).json(results);
 }
}

module.exports = {
  makeTaskRoute
}

Затем в index.js

const TodoService = require('todo/service/TodoService.js');
const getTasks = require('./routes/routes.js').makeTaskRoute(TodoService);
const router = express.Router()

router.get('/tasks', getTasks);

Это более функциональный стиль, но он также позволяет внедрять зависимости и легко тестировать.

Еще многое нужно сделать, чтобы сделать это чище, и многое - это предпочтения.Но я обнаружил, что это (плюс использование объявленных типов в машинописи и четкое разделение структур данных и мутаций) является чистым, легким для чтения, простым в обслуживании и улучшении.

1 голос
/ 24 сентября 2019

Понятия здесь действительны, но это псевдокод, я не запускал его.Кроме того, любые термины в «кавычках» не являются правильной терминологией

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

В вашем случае, когда вы можете выдать ошибку в службе и обработать ее в маршруте, лучше всего оставить вашу службу в качестве «чистой функции», чтобы подняться выше.Я считаю, что это подсознательно позволит вам также избежать круговых зависимостей .Случай, когда вы можете использовать try / catch на обоих «уровнях», может быть, если вы хотите выдавать пользовательские ошибки.

Примером этого является:

// Router
Router.get('/tasks', async (req, res) => {
    try { 
        return await TodoService.getTasksById(taskId);
    } catch (err) {
        return errorResponseUtility(err.status, err.message);
    }
}

// Service
const getTasksById = async id => {
    try {
        return await DB()
            .connect('tasks')
            .select('*')
            .where({ id });
    } catch (err) {
        if(err instanceOf NotFoundError) throw err;
    }
}

// Error Defs
const NotFoundError = {
    status: 404,
    message: 'Resource could not be found'
}

Таким образом, если у вас нет ничего, вы можете ошибиться, но правильным образом против случайного 502

Другое: Уничтожьте ваши требования:

const { id: taskId } = req.params; // This renames id to taskId

В качестве альтернативы, не разрушайте его, просто проанализируйте его, так как он используется только один раз.

1 голос
/ 24 сентября 2019

К сожалению, ОП отредактировал свой вопрос, чтобы сделать его другим ПОСЛЕ того, как я написал этот ответ.Этот ответ был написан в оригинальной версии вопроса.

Либо работает нормально, и это действительно вопрос мнения, которое понравится.

Во-первых, вы должны убедиться, что вызов метода НИКОГДА не вернет отклоненное обещание, поэтому вам не нужно отлавливать отклонения в вызывающей стороне.

Во втором случае вы можете получить возвращенное отклоненное обещание, поэтому вам нужно поймать отклонения в вызывающем абоненте.

Лично, это выглядит как ошибка кодирования всякий раз, когда я вижу await, у которого нет кода, который когда-либо ловит отклонение, поэтому вам придется хорошо документировать, что метод НИКОГДА не отклоняет.Я лично думаю, что это более гибко, если ошибки отклоняются, и вызывающая сторона может затем решить, что делать.Это позволяет использовать этот метод более широкому кругу вызывающих абонентов, но затем требует от вызывающего абонента отлавливать отклонения.

С другой стороны, если ВСЕ возможные варианты использования функции находятся в обработчиках запросов, где все, что вам нужнодля этого нужно вернуть статус без отклонений, чтобы его можно было отправить обратно в качестве ответа, тогда вы, вероятно, сохраняете код, перехватывая ошибки централизованно в функции, а не в каждой вызывающей стороне.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...