我最近偶然发现了这个问题,经过一段时间的阅读后,我无法找到满足此用例的答案。Javascript覆盖回调的范围
我想实现以下行为在JavaScript
// Lets assume we have some variable defined in global scope
var a = {val: 0}
// What I want here is a function that sets a.val = newVal
// and then calls the callback.
var start = function(newVal, cb) {
???
}
// such that
start(1, function() {
setTimeout(function() {
console.log(a.val) // 1
}, 1000)
})
// and
start(2,function() {
console.log(a.val) // 2
})
// but in the original scope
console.log(a.val) // 0
换句话说,我要寻找一个方式在不同的全球范围内“包装”的回调。我知道你可以做类似的事情,传递一个环境或使用它;但这种方法总是迫使回调函数来引用环境明确,把回调代码转换成类似
start(2,function() {
console.log(env.a.val) // 2
})
我专门找直接从回调中保留使用全球参考的可能性的解决方案的开始。
随意使用任何ES6/ES7功能,可以以某种方式将其擦除或与节点兼容,但这并不意味着生产代码只是一个有趣的练习。
编辑: 我会解释一般问题,因为许多人建议这个解决方案可能不是我真正需要的。
我最近了解到STM(https://wiki.haskell.org/Software_transactional_memory) ,并想在js中玩类似的想法。 当然,js在一个线程上运行,但是想法是为在原子块中运行的不同回调提供相同级别的隔离。
用户拥有某种共享事务变量。这个 变量的操作必须以原子方式包装。隐藏的内容是,原子块中的操作不是在实际的TVar上执行,而是在一些MockTVar上执行,它只记录日志中的所有读取和写入操作。 当您调用done时,将检查日志以查看所执行的操作是否与TVars的当前状态一致;如果是现在在实际的TVars上执行的更新并且我们完成了(这被称为提交)。如果不是,日志被丢弃并且回调再次运行。这是代码
var x = new TVar(2)
// this is process a
process.nextTick(function() {
atomically(x, function(x, done) {
a = x.readTVar()
setTimeout(function() {
x.writeTVar(a+1)
console.log('Process a increased, x = ', x.readTVar())
done()
}, 2000)
})
})
// this is process b
process.nextTick(function() {
atomically(x, function(x, done) {
var a = x.readTVar()
x.writeTVar(a+1)
console.log('Process b increased, x = ', x.readTVar())
done()
})
})
在该示例性过程的一个小例子一个将尝试提交但由于处理过程b改变x的值(和提交该改变之前的),提交将失败,回调将再次运行。
正如你所看到的,我在回调中返回了mockTVars,但是我觉得这有点难看,原因有两个:1)如果你想锁定多个变量(并且你通常会这样做),我别无选择但要返回一系列的mockTVars,如果他想干净地使用它们,则迫使用户逐一提取它们。 2)如果用户希望能够在不失主意的情况下推理发生的事情,则确保传递给回调函数的mockTVar的名称与实际的TVar的名称匹配。我的意思是,在这条线
atomically(x, function(x, done) {..})
它是由用户使用相同的名称都是指实际的TVar和嘲笑的TVar(名称为x在这个例子中)。
我希望这个解释很有帮助。感谢所有花时间帮助我的人
为什么你想这样做?确保这不是[xy问题](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – jeremy
我认为这种类型的事情只能在命令lisp与宏或Emacs lisp动态变量。 – jcubic
你基本上是问如何使用单个变量来存储多个值,这是不可能的。 – michael