我创建了一个NodeJS程序(Bluebird作为Promise库),它处理一些类似于下面代码片段工作的验证,但如果我运行该脚本,则会抛出以下错误:
Unhandled rejection RangeError: Maximum call stack size exceeded
显然,它正在重新分配我使用.bind(ctx)
的验证函数时进行一些递归函数调用
我解决这个问题的方法是将Promise工厂分配到obj._validate
,而不是重新分配obj.validate
并在需要的地方使用_validate(ctx)
。
但我仍然没有意识到为什么会发生错误。有人可以向我解释一下吗?
// Example validation function
function validate(pass, fail) {
const ctx = this
Promise.resolve(ctx.value) // Simulate some async validation
.then((value) => {
if (value === 'pass') pass()
if (value == 'fail') fail('Validation failed!')
})
}
let validations = [
{name: 'foo', validate: validate},
{name: 'bar', validate: validate},
{name: 'baz', validate: validate},
{name: 'qux', validate: validate}
]
// Reassigning validate functions to a promise factory
// to handle async validation
validations.forEach(obj => {
obj.validate = (ctx) => { // ctx used as context to validation
return new Promise(obj.validate.bind(ctx))
}
})
function executeValidations(receivedValues, validations) {
receivedValues.forEach((obj, i) => {
validations[i].validate(obj) // obj becomes the context to validate
.then(() => console.log('Validation on', obj.name, 'passed'))
.catch(e => console.error('Validation error on', obj.name, ':', e))
})
}
let receivedValues1 = [
{name: 'foo', value: 'pass'},
{name: 'bar', value: 'fail'},
{name: 'baz', value: 'fail'},
{name: 'qux', value: 'pass'},
]
executeValidations(receivedValues1, validations)
let receivedValues2 = [
{name: 'foo', value: 'pass'},
{name: 'bar', value: 'pass'},
{name: 'baz', value: 'fail'},
{name: 'qux', value: 'fail'},
]
executeValidations(receivedValues2, validations)
<script src="//cdn.jsdelivr.net/bluebird/3.4.7/bluebird.js"></script>
编辑:我认为这是问题的简短版本
function fn(res, rej) { return this.foo }
fn = function(ctx) { return new Promise(fn.bind(ctx))}
const ctx = {foo: 'bar'}
fn(ctx)
.then(console.log)
<script src="//cdn.jsdelivr.net/bluebird/3.4.7/bluebird.js"></script>
答案 0 :(得分:0)
obj.validate.bind(ctx)
评估异常函数对象,其this
值设置为ctx
。它仍然是一个功能对象。
然后出现
obj.validate = (ctx) => { // ctx used as context to validation
return new Promise(obj.validate.bind(ctx))
将obj.validate
设置为一个函数,该函数返回一个promise,在其构造期间同步调用其解析器函数obj.validate.bind(ctx)
(在ES6中也称为“executor function”),它返回一个promise其结构同步调用obj.validate.bind(ctx)
的对象,等等无限广告或JavaScript引擎会引发错误。
因此,第一次调用 obj.validate
会通过解析器函数启动无限循环的承诺生成。
使用bind
的另一个问题:
箭头函数在声明时绑定它们的词汇值。语法Function.prototype.bind
可以应用于箭头函数,但不会更改箭头函数看到的this
值!
因此,如果使用箭头函数定义方法,则obj.validate.bind(ctx)
永远不会更新this
中看到的obj.validate
值。
最大的问题可能是覆盖执行操作的函数的值:
发布时间:
validations.forEach(obj => {
obj.validate = (ctx) => { // ctx used as context to validation
return new Promise(obj.validate.bind(ctx))
}
会覆盖每个validate
条目的validations
属性。此属性曾经是在开始时声明的命名函数validate
,但不再是。
在简短版本中,
function fn(res, rej) { return this.foo }
fn = function(ctx) { return new Promise(fn.bind(ctx))}
const ctx = {foo: 'bar'}
fn(ctx)
fn = function...
会覆盖fn
的命名函数声明。这意味着,稍后调用fn
时,fn
的{{1}}会引用fn.bind(ctx)
的更新版本,而不是原始版本。
另请注意,解析器函数必须调用其第一个函数参数(fn
)来同步解析新的promise。忽略旋转变压器功能的返回值。
答案 1 :(得分:0)
executeValidations()要求validate()返回一个promise,因此最好返回一个promise。在验证过程中出现问题时拒绝承诺很有用,但验证测试失败是验证过程的正常部分,而不是错误。
import java.awt.BorderLayout;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComboBox;
import javax.swing.JFrame;
class Item {
int intValue;
String strValue;
public Item(int intValue, String strValue) {
this.intValue = intValue;
this.strValue = strValue;
}
public String toString() {
return intValue + " - " + strValue;
}
}
public class TestCombo {
private static JComboBox<Item> cb;
public static void main(String[]args) {
JFrame f = new JFrame();
f.setSize(640,400);
cb = new JComboBox<>();
cb.addItem(new Item(1, "one"));
cb.addItem(new Item(2, "two"));
cb.addItem(new Item(3, "three"));
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(cb, BorderLayout.NORTH);
f.setVisible(true);
selectItemByString("three");
}
private static void selectItemByString(String s) {
for (int i=0; i<cb.getItemCount(); i++) {
if (cb.getItemAt(i).strValue.equals(s)) {
cb.setSelectedIndex(i);
break;
}
}
return;
}
}
现在,executeValidations()可以将验证映射到错误列表
// Example validation function
function validate(ctx) {
return new Promise((resolve, reject) => {
// Perform validation asynchronously to fake some async operation
process.nextTick(() => {
// Passing validations resolve with undefined result
if (ctx.value === 'pass') resolve()
// Failing validations resolve with an error object
if (ctx.value == 'fail') resolve({
name: ctx.name,
error: 'Validation failed!'
})
// Something went wrong
reject('Error during validation')
})
})
}
如果没有错误,验证成功...
function executeValidations(receivedValues, validations) {
// Call validate for each received value, wait for the promises to resolve, then filter out any undefined (i.e. success) results
return Promise.all(receivedValues.map( obj => validate(obj)))
.then(results => results.filter(o => o !== undefined))
}