简体   繁体   English

JavaScript覆盖了回调的范围

[英]Javascript overwrite the scope of a callback

I stumbled into this problem recently and after a while of reading around I couldn't find an answer that satisfies this use case in particular. 最近,我偶然发现了这个问题,经过一段时间的阅读,我找不到一个特别满足该用例的答案。

I am trying to achieve the following behaviour in 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

In other words i am looking for a way to "wrap" a callback in a different global scope. 换句话说,我正在寻找一种在另一个全局范围内“包装”回调的方法。 I am aware that you can do something similar passing an environment around or using this; 我知道您可以在周围环境或使用环境中做类似的事情; but such methods always force the callback functions to refer to the environment explicitly, turning the callback code into something like 但是此类方法始终会强制回调函数显式引用环境,从而将回调代码变成类似

start(2,function () {
  console.log(env.a.val) // 2
})

I am specifically looking for a solution that preserves the possibility to use the global reference directly from within the callback of start. 我特别在寻找一种解决方案,该解决方案保留了直接从start回调中使用全局引用的可能性。

Feel free to use any ES6/ES7 feature that can somehow be shimmed in or is compatible with node, this is not meant for production code just a fun exercise. 可以随意使用可以以某种方式填充或与节点兼容的任何ES6 / ES7功能,这并不意味着生产代码只是一个有趣的练习。

EDIT: I will explain the general problem since many people suggested this solution might not be what I am actually looking for. 编辑:我将解释一般的问题,因为许多人建议此解决方案可能不是我真正想要的。

I recently learned about STM ( https://wiki.haskell.org/Software_transactional_memory ) and wanted to play around with a similar idea in js. 我最近了解了STM( https://wiki.haskell.org/Software_transactional_memory ),并想在js中尝试类似的想法。 Of course js runs on a single thread but the idea was to provide the same level of isolation to different callbacks running in atomic blocks. 当然,js运行在单个线程上,但其想法是为原子块中运行的不同回调提供相同级别的隔离。

The user has some kind of shared transactional variable. 用户具有某种共享的事务变量。 Operations on this variable must be wrapped in atomically blocks. 此变量的操作必须包装在原子块中。 What happens under the hood is that operations in the atomically block are not performed on the actual TVar but on some MockTVar which simply records all the reads and writes in a log. 实际情况是,原子块中的操作不是在实际的TVar上执行的,而是在某些MockTVar上执行的,该MockTVar只是将所有读取和写入记录在日志中。 When you call the done the log is checked to see if the operations performed are consistent with the current state of the TVars; 当您调用完成时,将检查日志以查看执行的操作是否与TVar的当前状态一致; if it is the updates now performed on the actual TVars and we are done (this is called a commit). 如果这是现在在实际TVar上执行的更新,那么我们就完成了(这称为提交)。 If it is not the log is discarded and the callback is run again. 如果不是,则丢弃日志,然后再次运行回调。 This is a small example of the code 这是代码的一个小例子

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()
 })

}) })

In this example process a will try to commit but since process b changed the value of x (and committed that change before a) the commit will fail and the callback will run once more. 在此示例中,进程a将尝试提交,但是由于进程b更改了x的值(并在a之前提交了该更改),提交将失败并且回调将再次运行。

As you can see I am returning the mockTVars in the callback, but i find this a bit ugly for two reasons: 1) If you want to lock more than one variable (and you generally do) i have no choice but to return an array of mockTVars forcing the user to extract them one by one if he wants to use them cleanly. 如您所见,我在回调函数中返回了mockTVars,但是由于以下两个原因,我发现这有点难看:1)如果您想锁定多个变量(通常这样做),我别无选择,只能返回一个数组如果用户想干净地使用它们,则必须强迫他们逐一提取它们。 2) It is up to the user to make sure that the name of the mockTVar which is passed to the callback matches the name of the actual TVar if he wants to be able to reason about whats happening without losing his mind. 2)如果用户希望能够推理出所发生的事情而又不会失去理智,则由用户确定传递给回调函数的嘲笑TVar的名称与实际TVar的名称匹配。 What I mean is that in this line 我的意思是说这句话

atomically(x, function(x, done) {..})

It is up to the user to use the same name to refer both to the actual TVar and the mocked TVar (the name is x in this example). 用户可以使用相同的名称来指代实际的TVar和模拟的TVar(在此示例中,名称为x)。

I hope this explanation is helpful. 我希望这个解释会有所帮助。 Thanks to everybody that took the time to help me out 感谢所有花时间帮助我的人

I still wish you would describe the actual problem you're trying to solve, but here's one idea, that makes a copy of the global object, passes it to the callback and the callback can use the same name as the global and then it will "override" access to the global for that scope only. 我仍然希望您能描述您要解决的实际问题,但这是一个想法,它会复制全局对象,并将其传递给回调,并且回调可以使用与全局相同的名称,然后它将仅在该范围内“覆盖”对全局的访问。

  var a = {val: 0, otherVal: "hello"} ; function start(newVal, cb) { var copy = {}; Object.assign(copy, a); copy.val = newVal; cb(copy); } log("Before start, a.val = " + a.val); start(1, function(a) { // locally scoped copy of "a" here that is different than the global "a" log("Beginning of start, a.val = " + a.val) // 1 a.val = 2; log("End of start, a.val = " + a.val) // 2 }); log("After start, a.val = " + a.val); function log(x) { document.write(x + "<br>"); } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM