0

我有一个这样的forloop:

for (var name in myperson.firstname){
  var myphone = new phone(myperson, firstname);
    myphone.get(function(phonenumbers){
      if(myphone.phonearray){
      myperson.save();
      //Can I put a break here?; 
    }
  });
}

它的作用是根据各种名字在数据库中搜索电话号码。我想要实现的是,一旦它找到与任何名字关联的数字,它就会执行 myperson.save 然后停止所有迭代,这样就不会保存重复项。有时,没有一个名字返回任何电话号码。

myphone.get包含服务器请求,成功时触发回调

如果我在响应中设置一个中断,那么循环的其他迭代会发生什么?很可能已经启动了其他 http 请求。我不希望他们执行保存。我想到的一种解决方案是将变量放在 forloop 之外并将其设置为保存,然后检查其他回调何时被触发,但我不确定这是否是最好的方法。

4

2 回答 2

1

您可以编写一个辅助函数来限制调用:

function callUntilTrue(cb) {
    var done = false;
    return function () {
        if (done) {
            log("previous callback succeeded. not calling others.");
            return;
        }
        var res = cb.apply(null, arguments);
        done = !! res;
    };
}

var myperson = {
    firstname: {
        "tom": null,
        "jerry": null,
        "micky": null
    },
    save: function () {
        log("save " + JSON.stringify(this, null, 2));
    }
};

var cb = function (myperson_, phonenumbers) {
    if (myperson_.phonearray) {
        log("person already has phone numbers. returning.");
        return false;
    }
    if (phonenumbers.length < 1) {
        log("response has no phone numbers. returning.");
        return false;
    }
    log("person has no existing phone numbers. saving ", phonenumbers);
    myperson_.phonearray = phonenumbers;
    myperson_.save();
    return true;
};

var restrictedCb = callUntilTrue(cb.bind(null, myperson));

for (var name in myperson.firstname) {
    var myphone = new phone(myperson, name);
    myphone.get(restrictedCb);
}

示例控制台:

results for tom-0 after 1675 ms
response has no phone numbers. returning.
results for jerry-1 after 1943 ms
person has no existing phone numbers. saving , [
  "jerry-1-0-number"
]
save {
  "firstname": {
    "tom": null,
    "jerry": null,
    "micky": null
  },
  "phonearray": [
    "jerry-1-0-number"
  ]
}
results for micky-2 after 4440 ms
previous callback succeeded. not calling others.

此 jsfiddle中带有假超时的完整示例。

编辑添加了 HTML 输出以及console.log.

于 2013-09-06T14:49:49.630 回答
0

第一个结果回调只会在循环之后发生,因为 javascript 的单线程特性以及如果事件到达时运行代码不会中断。

如果您仍然希望请求并行发生,您可以使用标志

var saved = false;
for (var name in myperson.firstname){
  var myphone = new phone(myperson, firstname /* name? */);
  myphone.get(function(phonenumbers){
    if (!saved && myphone.phonearray){
      saved = true;
      myperson.save();
    }
  });
}

这不会取消任何挂起的请求,但是,只会在它们返回后阻止保存。如果您.get()返回一些可取消的东西(也许是请求本身)会更好。

var saved = false;
var requests = [];
for (var name in myperson.firstname){
  var myphone = new phone(myperson, firstname /* name? */);
  var r;
  requests.push(r = myphone.get(function(phonenumbers){
    // Remove current request.
    requests = requests.filter(function(i) {
      return r !== i;
    });
    if (saved || !myphone.phonearray) {
      return;
    }
    saved = true;
    // Kill other pending/unfinished requests.
    requests.forEach(function(r) {
      r.abort();
    });
    myperson.save();
  }));
}

更好的是,不要一次启动所有请求。而是构造一个包含所有可能组合的数组,有一个计数器(一个信号量)并且只启动 X 个请求。

var saved = false;
var requests = [];
// Use requests.length as the implicit counter.
var waiting = []; // Wait queue.
for (var name in myperson.firstname){
  var myphone = new phone(myperson, firstname /* name? */);
  var r;
  if (requests.length >= 4) {
    // Put in wait queue instead.
    waiting.push(myphone);
    continue;
  }
  requests.push(r = myphone.get(function cb(phonenumbers){
    // Remove current request.
    requests = requests.filter(function(i) {
      return r !== i;
    });
    if (saved) {
      return;
    }
    if (!myphone.phonearray) {
      // Start next request.
      var w = waiting.shift();
      if (w) {
        requests.push(w.get(cb));
      )
      return;
    }
    saved = true;
    // Kill other pending/unfinished requests.
    requests.forEach(function(r) {
      r.abort();
    });
    myperson.save();
  }));
}
于 2013-09-06T14:43:54.693 回答