我有一条API路由被重构以使用ES6承诺来避免回调地狱。
成功转换为承诺链后,我希望将.then()
函数导出到单独的文件中,以保证清晰和清晰。
这很好用。但是,我 喜欢 要做的是将类constructor()
函数中声明的函数移动到独立的方法中,这些方法可以引用构造函数实例化的值。这样一切都读得更好。
但是,当我这样做时,我遇到了范围问题 - this
没有定义,等等。这样做的正确方法是什么? ES6适合在这里使用,还是应该使用其他结构?
RAW CODE:
路线... 的
.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);
}
}
}
答案 0 :(得分:1)
替代#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()...
标记每个方法,正如我最初想要的那样,它确实需要在路由文件中执行额外的步骤 - 以这种方式执行操作会阻止我分配特定的命名空间方法。
路线文件
.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);
});
})
当POST请求到达路由时,重置函数以反映每个新的req
和res
对象,并且this
关键字显然绑定到每个中的POST路由回调。进口方法。
重要说明:您无法使用此方法导出箭头功能。导出的函数必须是传统的匿名函数。这就是为什么,根据Udo G对同一主题的评论:
值得注意的是,这是有效的,因为函数中的
this
是直接调用函数时的全局范围(不以任何方式绑定)。
替代#2:
另一种选择,Bergi来自How to use arrow functions (public class fields) as class methods?
的礼貌我真正想要的是一个实验性功能......
有一个建议可能允许你省略构造函数()并直接将赋值放在具有相同功能的类作用域中,但我不建议使用它,因为它是高度实验性的。
但是,仍然有办法分离方法:
或者,您始终可以使用.bind,它允许您在原型上声明方法,然后将其绑定到构造函数中的实例。这种方法具有更大的灵活性,因为它允许从类外部修改方法。
基于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
}
}
显然,这与我最初寻找的内容更为一致,因为它增强了导出代码的整体可读性 BUT 这样做会要求你像路径文件一样为路由文件中的新类分配一个名称空间:
let SubmitRouteFunctions = require('./functions/submitFunctions.js');
let fn = new SubmitRouteFunctions(req, res);
Promise.all([fn.redundancyCheck, fn.getLocationInfo])
.then(...)
建议/实验特征:
这不是我的驾驶室,但根据Bergi,目前有一个第二阶段提案(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中执行此操作,这使得这几乎没有实际意义:)