【问题标题】:Injecting data into asynchronous callbacks (with node.js)将数据注入异步回调(使用 node.js)
【发布时间】:2012-08-13 17:58:07
【问题描述】:

我正在努力思考如何使异步编程工作。

在我当前的用例中,我的函数可能每秒被调用多次,并且它们具有依赖于多个变量的回调,这些变量可能会在它们之间发生变化。

一个简化的例子:(为简洁起见使用coffeescript)

doSomething = (requestor, thing, action, callback) ->
  thing.takeAction action, (result) ->
    # actually a lot of times this nests down even further
    requestor.report result
    callback result

如果在 thing.takeAction 返回结果之前使用不同的数据多次调用 doSomething,我认为我不能依赖请求者和回调仍然是我需要的相同事物。 对吗?

为了避免这种情况,我需要以某种方式将请求者和回调注入到 takeAction 的回调中。 这有可能吗?

我想到了做类似的事情

doSomething = (requestor, thing, action, callback) ->
  thing.takeAction action, (result, _requestor = requestor, _callback = callback) ->
    _requestor.report result
    _callback result

但这当然只是一个 CoffeeScript hack,根本不起作用。


顺便说一句,我试图使用 caolan/async 模块来帮助我解决这个问题,但事实仍然是,我在回调中经常需要比 async 提供的变量更多的变量。 喜欢:

doSomething = function(requestor, thing, action, callback) {
  // this might not need a waterfall, but imagine it would have nested further

  async.waterfall(
  [
    function(next) {
      thing.takeAction(action, function(result) {
        // How can I know that action is still the same?
        next(null, result);
      });
    }, 
    function(result, next) {
      requestor.report(result); // requestor still the same?
      next(null, result);
    }
  ],

  function(err, result) {
    callback(result); // callback still the same?
  });
}

它仍然给我留下同样的问题。那我该怎么做呢?

感谢您的宝贵时间。

【问题讨论】:

  • “动作还是一样”是什么意思?您是否担心您引用的 action 对象的某些字段可能会在事件循环中的其他位置发生变化?如果是这种情况,显而易见的解决方案是在 doSomething 正文中克隆此 action 对象。
  • 是的,动作对象一直在变化,甚至在回调被调用之前。甚至在第一个回调返回之前,该函数也会被不同的动作对象调用。就像doSomething(x, y, action1, cb); doSomething(x, y, action2, cb); doSomething(x, y, action3, cb); 我假设这也会改变回调上下文中的'action'变量。 ... doSomething-body 中的克隆仍然会在回调中持续存在吗?因为我现在正在跟踪一些奇怪的错误,这让我认为它没有。

标签: javascript node.js asynchronous callback coffeescript


【解决方案1】:

我不确定使用 CS 是否真的对这个例子有帮助,让我用普通的 JS 来说明:

var doSomething = function (requestor, thing, action, callback) {
    thing.takeAction(action, function (result) {
        requestor.report(result);
        callback(result);
    });
};

// Following is completely safe:
doSomething(r1, t1, a1, c1); 
doSomething(r2, t2, a2, c2); 
doSomething(r3, t3, a3, c3); 

每次调用 doSomething 时,都会创建新的函数范围。所以在内部,创建并传递给 takeAction 的函数可以访问最初调用 doSomething 的参数(对 doSomething 的另一个调用不会改变这一点!)。这就是 JavaScript 中作用域的工作方式

【讨论】:

    【解决方案2】:

    您需要将action 对象内容与action 值本身分开。也就是说,内存中有一些对象,在这个特定的上下文中被action名称引用。

    例如,

    function test(action) {
        alert("Test1: " + action.value);
        setTimeout(function () { alert("Test2: " + action.value); }, 1000);
    }
    
    var action = { value: 1; };
    test(action);
    action = { value: 2 };
    alert("Action value outside: " + action.value);
    

    将警告“Test1: 1”、“Action value outside: 2”和“Test1: 1”。但是,一旦将action = { value: 2 }; 替换为action.value = 2,最后一个警报将变为“Test1: 2”。

    所以,如果您的问题是操作对象的某些字段在外部发生了更改,只需将其克隆到您的 doSomething 的第一行。如果您的问题是对对象的引用在外部发生了更改,您不必担心,它不会以任何方式影响您的 doSomething

    此外,对 doSomething 的后续调用不会“覆盖”回调中 action 参数的值,因为它在特定调用中被关闭:en.wikipedia.org/wiki/Closure_(computer_science)

    例如,

    function test(action) {
        alert("Test1: " + action.value);
        setTimeout(function () { alert("Test2: " + action.value); }, 1000);
    }
    
    var action = { value: 1; };
    test(action);
    action = { value: 2 };
    test(action);
    

    将提醒“Test1: 1”、“Test1: 2”、“Test2: 1”和“Test2: 2”(不是“Test1: 1”、“Test1: 2”、“Test2: 2”和“Test2 : 2" 就像你害怕的那样)。

    【讨论】:

      猜你喜欢
      • 2017-03-20
      • 2020-06-04
      • 1970-01-01
      • 2015-01-22
      • 2019-01-05
      • 2013-12-15
      • 2012-06-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多