我正在重构一个 API 路由,以使用 ES6 Promise 来避免回调 hell 。
成功转换为 promise 链后,我想将我的 .then()
函数导出到一个单独的文件中,以保持整洁和清晰。
这很好用。但是,我喜欢要做的是将类constructor()
函数中声明的函数移动到独立的方法中,这些方法可以引用由构造函数实例化的值。这样读起来就更好了。
但是,当我这样做时,我遇到了范围界定问题 - this
未定义,等等。执行此操作的正确方法是什么? ES6 适合在这里使用,还是应该使用其他结构?
原始代码:
路线...
.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);
}
}
}
最佳答案
替代#1:
与使用 ES6 类不同,执行相同行为并稍微清理代码的另一种方法是导出匿名函数,如 Nick Panov 中所述。这里:In Node.js, how do I "include" functions from my other files?
函数文件:
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()...
标记每个方法正如我最初想要的那样,它确实在路由文件中采取了额外的步骤 - 以这种方式执行操作可以使我不必为方法分配特定的 namespace 。
路线文件
.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);
});
})
功能被重置以反射(reflect)每个新的 req
和res
对象作为 POST 请求到达路由,并且 this
关键字显然绑定(bind)到每个导入方法中的 POST 路由回调。
重要提示:您无法使用此方法导出箭头函数。导出的函数必须是传统的匿名函数。这就是原因,根据 Udo G对同一主题的评论:
It should be worth to note that this works because
this
in a function is the global scope when the function is called directly (not bound in any way).
替代#2:
另一种选择,由 Bergi 提供来自:How to use arrow functions (public class fields) as class methods?
我真正寻找的是一个实验性功能......
There is an proposal which might allow you to omit the constructor() and directly put the assignment in the class scope with the same functionality, but I wouldn't recommend to use that as it's highly experimental.
但是,仍然有一种方法可以分离方法:
Alternatively, you can always use .bind, which allows you to declare the method on the prototype and then bind it to the instance in the constructor. This approach has greater flexibility as it allows modifying the method from the outside of your class.
基于 Bergi 的示例:
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(...)
提议/实验功能:
这并不是我真正的驾驶室,但根据Bergi ,目前有一个 Stage-2 提案 ( https://github.com/tc39/proposal-class-public-fields ) 正在尝试将“类实例字段”添加到下一个 ES 规范中。
"Class instance fields" describe properties intended to exist on instances of a class (and may optionally include initializer expressions for said properties)
据我了解,这将通过允许附加到 class
的方法来完全解决这里描述的问题对象来引用其自身的每个实例。因此,this
问题就会消失,并且可以选择自动绑定(bind)方法。
我(有限)的理解是箭头函数将用于完成此操作,如下所示:
class SomeClass {
constructor() {...}
someMethod (val) => {
// Do something with val
// Where 'this' is bound to the current instance of SomeClass
}
}
显然,现在可以使用 Babel 编译器来完成此操作,但显然是实验性的且有风险。另外,在本例中,我们尝试在 Node/Express 中执行此操作,这使得这几乎成为一个没有实际意义的问题:)
关于javascript - 为 promise 链导出 Express 路由方法的最佳方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44379482/