gpt4 book ai didi

javascript - Node.js — 超过最大调用堆栈大小,即使使用 process.nextTick()

转载 作者:行者123 更新时间:2023-11-30 07:33:50 25 4
gpt4 key购买 nike

我正在尝试为“可链接的”Express.js 验证编写一个模块:

const validatePost = (req, res, next) => {
validator.validate(req.body)
.expect('name.first')
.present('The parameter is required')
.string('The parameter must be a string')
.go(next);
};

router.post('/', validatePost, (req, res, next) => {
return res.send('Validated!');
});

validator.validate 的代码(为简洁起见进行了简化):

const validate = (data) => {

let validation;

const expect = (key) => {
validation.key = key;

// Here I get the actual value, but for testing purposes of .present() and
// .string() chainable methods it returns a random value from a string,
// not string and an undefined
validation.value = [ 'foo', 123, void 0 ][Math.floor(Math.random() * 3)];
return validation;
};

const present = (message) => {
if (typeof validation.value === 'undefined') {
validation.valid = false;
validation.errors.push({ key: validation.key, message: message });
}
return validation;
};

const string = (message) => {
if (typeof validation.value !== 'string') {
validation.valid = false;
validation.errors.push({ key: validation.key, message: message });
}
return validation;
};

const go = (next) => {
if (!validation.valid) {
let error = new Error('Validation error');
error.name = 'ValidationError';
error.errors = validation.errors;

// I even wrap async callbacks in process.nextTick()
process.nextTick(() => next(error));
}
process.nextTick(next);
};

validation = {
valid: true,
data: data,
errors: [],
expect: expect,
present: present,
string: string,
go: go
};

return validation;

};

该代码适用于短链,返回正确的错误对象。但是,如果我链接了很多方法,请说:

const validatePost = (req, res, next) => {
validator.validate(req.body)
.expect('name.first')
.present('The parameter is required')
.string('The parameter must be a string')
.expect('name.first') // Same for testing
.present('The parameter is required')
.string('The parameter must be a string')
// [...] 2000 times
.go(next);
};

Node.js 抛出 RangeError: Maximum call stack size exceeded。请注意,我将异步回调 .go(next) 包装在 process.nextTick() 中。

最佳答案

我没有太多时间看这个,但我确实注意到了一个相当大的问题。当 !validator.validtrue 时,您有一个单分支 if 语句导致 next 被调用两次 .一般来说,单分支 if 语句是一种代码味道。

这可能不是您遇到堆栈溢出的原因,但它可能是罪魁祸首。

(代码更改以粗体显示)

const go = (next) => {
if (!validation.valid) {
let error = new Error('Validation error');
error.name = 'ValidationError';
error.errors = validation.errors;
process.nextTick(() => next(error));
}
<b>else {</b>
process.nextTick(next);
<b>}</b>
};

有些人也使用 return 来欺骗 if。这也行,但很烂

const go = (next) => {
if (!validation.valid) {
let error = new Error('Validation error');
error.name = 'ValidationError';
error.errors = validation.errors;
process.nextTick(() => next(error));
<b>return;</b> // so that the next line doesn't get called too
}
process.nextTick(next);
};

我认为整个go函数这样表达更好...

const go = (next) => {
// `!` is hard to reason about
// place the easiest-to-understand, most-likely-to-happen case first
if (validation.valid) {
process.nextTick(next)
}
// very clear if/else branching
// there are two possible outcomes and one block of code for each
else {
let error = new Error('Validation error');
error.name = 'ValidationError';
error.errors = validation.errors;
// no need to create a closure here
<del>process.nextTick(() => next(error));</del>
<b>process.nextTick(next, error);</b>
}
};

其他备注

您的代码中还有其他单分支 if 语句

const present = (message) => {
if (typeof validation.value === 'undefined') {
// this branch only performs mutations and doesn't return anything
validation.valid = false;
validation.errors.push({ key: validation.key, message: message });
}
// there is no `else` branch ...

return validation;
};

这个不那么令人反感,但我仍然认为,一旦您欣赏了总是带有 elseif 语句,就更难推理了。考虑强制两个分支的三元运算符 (?:)。还要考虑像 Scheme 这样的语言,其中在使用 if 时始终需要 TrueFalse 分支。

这是我编写present 函数的方式

const present = (message) => {
if (validation.value === undefined) {
// True branch returns
return Object.assign(validation, {
valid: false,
errors: [...validation.errors, { key: validation.key, message }]
})
}
else {
// False branch returns
return validation
}
};

这是一个自以为是的评论,但我认为它值得考虑。当您必须返回此代码并稍后阅读时,您会感谢我。当然,一旦您的代码采用这种格式,您就可以对其进行加糖以删除大量语法样板

const present = message =>
validation.value === undefined
<b>?</b> Object.assign(validation, {
valid: false,
errors: [...validation.errors, { key: validation.key, message }]
})
<b>:</b> validation

优点

  • 隐式 return 有效地强制您在函数中使用单个表达式——这意味着您不能(轻易地)使函数过于复杂
  • 三元表达式是一个表达式,而不是一个语句if 没有返回值,所以使用三元可以很好地处理隐式返回
  • 三元表达式将你限制为每个分支一个表达式——同样,迫使你保持代码简单
  • 三元表达式强制您同时使用 true false 分支,以便您始终处理 both谓词

是的,没有什么能阻止您使用 () 将多个表达式组合成一个,但关键不是要将每个函数简化为一个表达式——它更像是一种理想的和漂亮的在它工作时使用。如果在任何时候您觉得可读性受到影响,您可以求助于 if (...) { return ... } else { return ... } 以获得熟悉且友好的语法/样式。

关于javascript - Node.js — 超过最大调用堆栈大小,即使使用 process.nextTick(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40945955/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com