Правильный способ обработки обещаний и ответа сервера

Я пытаюсь улучшить свой код в node.js/sail.js, и я борюсь с ответом сервера в обещаниях.

Когда вы смотрите на первую функцию .then, вы можете увидеть, что метод возвращает false в случае forbidden access или notFound. Затем, в следующих функциях .then, я должен проверить, является ли тип возврата === false, чтобы перейти к разделу и избежать отправки заголовков HTTP дважды. Может ли это быть каким-то образом улучшено, чтобы пропустить все следующие методы .then в случае сбоя? Я могу бросить исключение, чтобы перейти в последний .catch, но тогда должно быть case для переключения между всеми возможными состояниями. (т.е. запрещено, serverError или даже не найден)

Notification.findOne({id: req.param('id')})
  .then(function(notification) {
    if (!notification) {
      res.notFound();
      return false;
    }

    if (notification.triggeredBy != req.session.user.id) {
      res.forbidden();
      return false;
    }

    return notification;
  })
  .then(function(notification) {
    if (notification === false) {
      return false;
    }

    return Notification.update(notification.id, actionUtil.parseValues(req));
  })
  .then(function(notification) {
    if (notification === false) {
      return false;
    }

    res.json(notification);
  })
  .catch(function(err) {
    sails.log(err);
    res.serverError({message: 'A server error occurred.'});
  })

Ответ 1

Если бы я сделал это, сначала я отдельная логика и функция получения/отправки. Во-вторых, я указываю список кодов ошибок. И это будет так:

NotificationService.js

/*
 Listing of error codes: {
  * [1] Object not found
  * [2] Forbidden
  * [3] Server error
 }
 */
module.exports = {
    nameOfMethod: function(ID, sessionID) {

        return new Promise(function(resolve, reject) {
            Notification.findOne({ id: ID })
                .then(function(notification) {
                    if (!notification) return reject({ error_code: 1 });
                    if (notification.triggeredBy !== sessionID) return reject({ error_code: 2 });

                    Notification.update(notification.id, actionUtil.parseValues(req))
                        .then(function(notification) {
                            return resolve(notification); // finally return our notification
                        })
                        .catch(function(err) {
                            sails.log.error(err); // It good when log is classified. In this case is error
                            return reject({ message: 'A server error occurred.' }); 
                        });
                })
                .catch(function(err) {
                    sails.log.error(err);
                    return reject({ message: 'A server error occurred.' });
                });
        });
    }
};

NotificationController.js

module.exports = {
  notifyMe: function(req, res) {
    const ID = req.param('id'), sessionID = req.session.user.id;

    NotificationService.nameOfMethod(ID, sessionID)
      .then(function(notification) {
        return res.send(notification);
      })
      .catch(function(err) {
        switch (err.error_code) {
          case 1:
            return res.notFound(err);

          case 2:
            return res.forbidden(err);

          default:
            return res.serverError(err);
        }
      });
  }
};

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

Ответ 2

Посмотрите, как в Bluebird реализовано отфильтрованное .catch() - в вашем случае может оказаться полезным все необходимые вам ошибки, но избегайте использования большого блока переключателя/случая в обработчике catch:

.catch(
    class ErrorClass|function(any error)|Object predicate...,
    function(any error) handler
) -> Promise
.caught(
    class ErrorClass|function(any error)|Object predicate...,
    function(any error) handler
) -> Promise

Это расширение для .catch, чтобы работать больше как catch-clauses в таких как Java или С#. Вместо ручной проверки instanceof или .name === "SomeError", вы можете указать ряд конструкторов ошибок которые подходят для этого обработчика улова. Обработчик уловов, который первый встреченный, который имеет соответствующие конструкторы, указан, тот, который будет вызываться.

Пример:

somePromise.then(function() {
    return a.b.c.d();
}).catch(TypeError, function(e) {
    //If it is a TypeError, will end up here because
    //it is a type error to reference property of undefined
}).catch(ReferenceError, function(e) {
    //Will end up here if a was never declared at all
}).catch(function(e) {
    //Generic catch-the rest, error wasn't TypeError nor
    //ReferenceError
});

Смотрите: http://bluebirdjs.com/docs/api/catch.html#filtered-catch

Вместо:

return false;

вы можете использовать:

return Promise.reject(someReason);

или

throw someReason;

и вам не нужно будет проверять эти значения false - просто используйте (возможно, несколько) обработчиков catch.