0

I have a forloop like this:

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?; } }); } 

What it does is that it searches for phone-numbers in a database based on various first-names. What I want to achieve is that once it finds a number associated with any of the first names, it performs myperson.save and then stops all the iterations, so that no duplicates get saved. Sometimes, none of the names return any phone-numbers.

myphone.get contains a server request and the callback is triggered on success

If I put a break inside the response, what will happen with the other iterations of the loop? Most likely the other http-requests have already been initiated. I don't want them to perform the save. One solution I have thought of is to put a variable outside of the forloop and set it to save, and then check when the other callbacks get's triggered, but I'm not sure if that's the best way to go.

6
  • Do you mean you want to do the myphone.get calls sequentially until one has the result you are looking for? Or do you want to do them all and then choose which one to save? Commented Sep 6, 2013 at 14:26
  • Ideally, for performance, I'd like to keep them paralel, but if more than one finds a phone-number, I'd like to not save only the first. Commented Sep 6, 2013 at 14:26
  • 1
    Ok, then you would collect all the results in step 1 (wait for them all), then iterate over them and choose which one(s) to save in step 2. Commented Sep 6, 2013 at 14:27
  • How do I know when all the callbacks are done? Commented Sep 6, 2013 at 14:29
  • Which JS library(ies) are you using? They all have different async helper functions. Commented Sep 6, 2013 at 14:29

2 Answers 2

1

You could write a helper function to restrict invocations:

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

Sample Console:

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. 

Full example in this jsfiddle with fake timeouts.

EDIT Added HTML output as well as console.log.

Sign up to request clarification or add additional context in comments.

1 Comment

This solution is wrong, btw. It will call cb exactly once, without verifying that the result is indeed valid: the if(myphone.phonearray) part.
0

The first result callback will only ever happen after the loop, because of the single-threaded nature of javascript and because running code isn't interrupted if events arrive.

If you you still want requests to happen in parallel, you may use a flag

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

This will not cancel any pending requests, however, just prevent the save once they return. It would be better if your .get() would return something cancelable (the request itself, maybe).

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

Even better, don't start all requests at once. Instead construct an array of all possible combinations, have a counter (a semaphore) and only start X requests.

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.