1

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

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

一个简化的例子:(为简洁起见使用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?
  });
}

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

感谢您的时间。

4

2 回答 2

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”正如你似乎害怕的那样)。

于 2012-08-17T09:54:12.193 回答
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 中作用域的工作方式

于 2012-08-17T11:23:03.193 回答