Лучший способ экспортировать экспресс-методы маршрутов для цепочек обещаний?

1

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

После успешной конвертации в цепочку обещаний я хотел экспортировать свои функции .then() в отдельный файл для чистоты и ясности.

Файл маршрута: Изображение 174551

Файл функций: Изображение 174551

Это прекрасно работает. Однако то, что я хотел бы сделать, это переместить функции, объявленные в функции класса constructor() в независимые методы, которые могут ссылаться на значения, созданные конструктором. Таким образом, все это выглядит лучше.

Но когда я это делаю, я сталкиваюсь с проблемами обзора - this не определено и т.д. Каков правильный способ сделать это? Является ли ES6 соответствующим для использования здесь, или я должен использовать какую-либо другую структуру?

КОД RAW:

маршрут...

.post((req, res) => {

  let SubmitRouteFunctions = require('./functions/submitFunctions.js');
  let fn = new SubmitRouteFunctions(req, res);

  // *******************************************
  // ***** THIS IS WHERE THE MAGIC HAPPENS *****
  // *******************************************
  Promise.all([fn.redundancyCheck, fn.getLocationInfo])
         .then(fn.resetRedundantID)
         .then(fn.constructSurveyResult)
         .then(fn.storeResultInDB)
         .then(fn.redirectToUniqueURL)
         .catch((err) => {
           console.log(err);
           res.send("ERROR SUBMITTING YOUR RESULT: ", err);
         });
  })

экспортируемые функции...

module.exports = class SubmitRouteFunctions {

   constructor (req, res) {
this.res = res;
this.initialData = {
  answers    : req.body.responses,
  coreFit    : req.body.coreFit,
  secondFit  : req.body.secondFit,
  modules    : req.body.modules,
};

this.newId = shortid.generate();
this.visitor = ua('UA-83723251-1', this.newId, {strictCidFormat: false}).debug();
this.clientIp = requestIp.getClientIp(req);

this.redundancyCheck = mongoose.model('Result').findOne({quizId: this.newId});
this.getLocationInfo = request.get('http://freegeoip.net/json/' + this.clientIp).catch((err) => err);

this.resetRedundantID = ([mongooseResult, clientLocationPromise]) => {

    console.log(mongooseResult);
    if (mongooseResult != null) {
      console.log('REDUNDANT ID FOUND - GENERATING NEW ONE')
      this.newId = shortid.generate();
      this.visitor = ua('UA-83723251-1', this.newId, {strictCidFormat: false});
      console.log('NEW ID: ', this.newId);
    };
    return clientLocationPromise.data;
  }

this.constructSurveyResult = (clientLocation) => {
    let additionalData = {quizId: this.newId, location: clientLocation};
    return Object.assign({}, this.initialData, additionalData);
  }

this.storeResultInDB = (newResult) => mongoose.model('Result').create(newResult).then((result) => result).catch((err) => err);

this.redirectToUniqueURL = (mongooseResult) => {
  let parsedId = '?' + queryString.stringify({id: mongooseResult.quizId});
  let customUrl = 'http://explore-your-fit.herokuapp.com/results' + parsedId;
  this.res.send('/results' + parsedId);
}
  }
}
Теги:
express
es6-promise
es6-class

1 ответ

1
Лучший ответ

АЛЬТЕРНАТИВА № 1:

Вместо использования классов ES6 альтернативный способ выполнения такого же поведения, который немного очищает код, заключается в экспорте анонимной функции, описанной здесь пользователем Nick Panov: В Node.js, как мне включить функции из моего другие файлы?

ФАЙЛ ФУНКЦИЙ:

module.exports = function (req, res) {

    this.initialData = {
      answers    : req.body.responses,
      coreFit    : req.body.coreFit,
      secondFit  : req.body.secondFit,
      modules    : req.body.modules,
    };

    this.newId = shortid.generate();
    this.visitor = ua('UA-83723251-1', this.newId, {strictCidFormat: false}).debug();
    this.clientIp = requestIp.getClientIp(req);

    this.redundancyCheck = mongoose.model('Result').findOne({quizId: this.newId});
    this.getLocationInfo = request.get('http://freegeoip.net/json/' + this.clientIp).catch((err) => err);

    this.resetRedundantID = ([mongooseResult, clientLocationPromise]) => {
        if (mongooseResult != null) {
          console.log('REDUNDANT ID FOUND - GENERATING NEW ONE')
          this.newId = shortid.generate();
          this.visitor = ua('UA-83723251-1', this.newId, {strictCidFormat: false});
          console.log('NEW ID: ', this.newId);
        };
        return clientLocationPromise.data;
      }

    this.constructSurveyResult = (clientLocation) => {
        let additionalData = {quizId: this.newId, location: clientLocation};
        return Object.assign({}, this.initialData, additionalData);
      }

    this.storeResultInDB = (newResult) => mongoose.model('Result').create(newResult).then((result) => result).catch((err) => err);

    this.redirectToUniqueURL = (mongooseResult) => {
      let parsedId = '?' + queryString.stringify({id: mongooseResult.quizId});
      let customUrl = 'http://explore-your-fit.herokuapp.com/results' + parsedId;
      res.send('/results' + parsedId);
    }
}

Хотя это не this.someFn()... каждому методу this.someFn()..., как я и хотел, он делает дополнительный шаг в файле маршрутизации - это делает так, что мне не нужно назначать определенное пространство имен методы.

МАРШРУТЫ

.post((req, res) => {
          require('./functions/submitFunctions_2.js')(req, res);

          Promise.all([redundancyCheck, getLocationInfo])
                 .then(resetRedundantID)
                 .then(constructSurveyResult)
                 .then(storeResultInDB)
                 .then(redirectToUniqueURL)
                 .catch((err) => {
                   console.log(err);
                   res.send("ERROR SUBMITTING YOUR RESULT: ", err);
                 });
      })

Функции сбрасываются, чтобы отражать каждый новый объект req и res поскольку запросы POST попадают в маршрут, и this ключевое слово, по-видимому, связано с обратным вызовом маршрута POST в каждом из импортированных методов.

ВАЖНОЕ ПРИМЕЧАНИЕ. Вы не можете экспортировать функцию стрелки с помощью этого метода. Экспортированная функция должна быть традиционной анонимной функцией. Вот почему, за Udo G комментируют ту же тему:

Стоит отметить, что это работает, потому что this в функции является глобальной областью, когда функция вызывается непосредственно (никак не привязана).

АЛЬТЕРНАТИВА № 2:

Другой вариант, любезно предоставленный Берги из: Как использовать функции стрелок (public class fields) в качестве методов класса?

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

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

Тем не менее, есть еще способ разделить методы:

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

На примере Берги:

module.exports = class SomeClass {

  constructor() {
    this.someMethod= this.someMethod.bind(this);
    this.someOtherMethod= this.someOtherMethod.bind(this);
    …
  }

  someMethod(val) {
    // Do something with val
  }

  someOtherMethod(val2) {
    // Do something with val2
  }
}

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

let SubmitRouteFunctions = require('./functions/submitFunctions.js');
let fn = new SubmitRouteFunctions(req, res);

Promise.all([fn.redundancyCheck, fn.getLocationInfo])
       .then(...)

ПРЕДЛАГАЕМАЯ/ЭКСПЕРИМЕНТАЛЬНАЯ ХАРАКТЕРИСТИКА:

Это не моя рулевая рубка, но на Берги в настоящее время есть предложение Stage-2 (https://github.com/tc39/proposal-class-public-fields), которое пытается получить "поля экземпляра класса", добавленные к следующей спецификации ES.

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

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

Мое (ограниченное) понимание состоит в том, что функция стрелки будет использоваться для выполнения этого, например:

  class SomeClass {
      constructor() {...}
      someMethod (val) => {
        // Do something with val
        // Where 'this' is bound to the current instance of SomeClass
      }
    }

По-видимому, это можно сделать теперь, используя компилятор Babel, но явно экспериментальный и рискованный. Плюс, в этом случае мы пытаемся сделать это в Node/Express, что делает это практически спорным моментом :)

  • 0
    Лично я думаю, что предпочитаю метод № 1. Хотя это НЕ позволяет вам отделить методы от this.method = ... и, возможно, менее читабельно в экспортированном файле, оно делает цепочку обещаний в вашем экспресс-маршруте чистой, ясной и лаконичной - что было моей основной задачей цель на первом месте. Он также использует меньше кода, поскольку полностью исключает функцию constructor() , не требует нескольких строк объявлений .bind(this) и устраняет необходимость связывать экспортируемые методы с их собственным пространством имен. Мысли?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню