cigol on
cigol on

Reputation: 397

RESOLVED: how can i break a for loop based on a response from promise?

So I am writing a script to execute some things for me automatically on a site. so far I have figured everything out but I am doing some things incorrectly and just want to figure out a better way to do it.

I have a for loop that will send requests to an API via a fetch. The number of loops will be based on a variable. I want to be able to process the response from each fetch as they come in and if any of them ever shows up with a result of true I want it to stop my for loop from continuing. Currently the way I have it working is it will refresh the page (which will stop my loop) but this is very inefficient. I just needed to get it done somehow.

As for the synchronous/asynchronous i want it to send the requests regardless of the response, until the response is a true then I want it to stop sending requests. It is okay if I have a few requests over which is what happens now with my page refresh method.

Im open to suggestions and to revamp the whole thing (im just learning things as i go and didnt even know what js was 2 months ago. but for all the things I have figured out how to do, I cant seem to grasp this concept.)

I've tried naming the loop and breaking @ name, tried returning a break, and nothing ive tried has managed to work. The only thing that has is a page refresh which is one way to stop a loop i guess.

var reqs = 5000;
xId = 970;
xSym = mySymbol;

var looper = async function () {
  for (var start = 1; start < reqs; start++) {
    await new Promise(resolve => { 
        setTimeout(function () { 

            //This is essentially what I need to loop over and over.
            //but not any faster then 200 ms per request. 

            fetch("https://example.com/api/send", {"credentials":"include","body":`{\"xactionId\":\"${xId}\",\"symbol\":\"${xSym}\"}`,"method":"POST","mode":"cors"})
            .then(resp => resp.json())
            .then(json => {
            if(json.test.result === true) { 
                console.log(json.test.hash, json.test.number); 
                    //  
                    //This is where I want to be able to stop the loop
                    //If any of the completed results 
                    //come back true to have it discontinue the Looper
                    //
                    //currently I do a window.location.reload();
                    //  
            }})
            .catch(err => console.log(err));

            resolve(true);
          }, 200);
    });
  }
  return true;
}
looper().then(function(){
  console.log("Got a match!");
});

And just in case anyone needs the responses i get back from server.

{
  "result": {
    "hash": "dbbb42b293",
    "result": false,
    "isHigh": false,
    "number": 4993,
    "threshold": 3,
    "chance": 0.03,
    "nonce": 2194375,
    "created": 1554150935
  },
  "dailyFree": false,
  "status": null,
  "user": {
    "hash": "aabbccdd8f",
    "level": 300,
    "username": "user",
    "requests": 4440936,
    "nonce": 2194376,
    "volume": "11.10794076",
    "lockedBalance": null,
    "session": {
      "requests": 5,
      "volume": "0.000004"
    }
  }
}

I want to be able to stop the for loop, in the looper async function based on a result in the second .then after the fetch POST request.

Also, I am wondering if the above is possible while also having the fetch request in its own external function so that i can call it from other places in the code as well.


RESOLUTION: I used the last option that @Bergi suggested. Thanks for the help!

my final code looks like this:

var reqs = 5000;
xId = 970;
xSym = mySymbol;
function delay(t) {
    return new Promise(resolve => setTimeout(resolve, t));
}
async function looper() {
  var run = true;
  for (var start = 1; run && start < reqs; start++) {
    await delay(200);
    fetch("https://example.com/api/send", {"credentials":"include","body":`{\"xactionId\":\"${xId}\",\"symbol\":\"${xSym}\"}`,"method":"POST","mode":"cors"})
    .then(resp => resp.json())
    .then(json => {
      if (json.test.result === true) { 
        console.log(json.test.hash, json.test.number); 
        run = false;
      }
    });
  }
  return true;
}
looper().then(function(){
  console.log("DONE!")
});

Upvotes: 1

Views: 1917

Answers (2)

Bergi
Bergi

Reputation: 664620

Avoid the Promise constructor antipattern! You should use

function delay(t) {
    return new Promise(resolve => setTimeout(resolve, t));
}

and not use the Promise constructor elsewhere, especially wrapping around other promise calls. I guess you are looking for

async function looper() {
  for (var start = 1; start < reqs; start++) {
    await delay(200);
    const resp = await fetch("https://example.com/api/send", {"credentials":"include","body":`{\"xactionId\":\"${xId}\",\"symbol\":\"${xSym}\"}`,"method":"POST","mode":"cors"});
    const json = await resp.json();
    if (json.test.result === true) { 
      console.log(json.test.hash, json.test.number); 
      break;
//    ^^^^^
    }
  }
  return true;
}

Or maybe have the delay(200) and the fetch run in parallel, so that you are waiting at minimum 200ms not additionally to the time that fetch takes:

async function looper() {
  for (var start = 1; start < reqs; start++) {
    const [, json] = await Promise.all([
      await delay(200),
      fetch("https://example.com/api/send", {"credentials":"include","body":`{\"xactionId\":\"${xId}\",\"symbol\":\"${xSym}\"}`,"method":"POST","mode":"cors"}).then(resp => resp.json()),
    ]);
    if (json.test.result === true) { 
      console.log(json.test.hash, json.test.number); 
      break;
//    ^^^^^
    }
  }
  return true;
}

If you really wanted to fire off a fetch request every 200ms, you cannot use await here. You'd have to use a boolean variable in the looping condition that checks whether any of the already received responses wants the loop to stop:

async function looper() {
  var run = true;
  for (var start = 1; run && start < reqs; start++) {
//                    ^^^^^^
    await delay(200);
    fetch("https://example.com/api/send", {"credentials":"include","body":`{\"xactionId\":\"${xId}\",\"symbol\":\"${xSym}\"}`,"method":"POST","mode":"cors"})
    .then(resp => resp.json())
    .then(json => {
      if (json.test.result === true) { 
        console.log(json.test.hash, json.test.number); 
        run = false;
//      ^^^^^^^^^^^^
      }
    })
    .catch(err => console.log(err));
  }
  return true;
}

Upvotes: 2

Tiagojdferreira
Tiagojdferreira

Reputation: 1122

In your looper you have the loop and you do a await:

var looper = async function () {
  for (var start = 1; start < reqs; start++) {
    await new Promise(resolve => { ... })
  }
}

Await can be used to assign the result of your promise to a variable. This way you can control your loop outside the promises.

var looper = async function () {
  for (var start = 1; start < reqs; start++) {
    const continue = await new Promise(resolve => {
      setTimeout(function () { 

            //This is essentially what I need to loop over and over.
            //but not any faster then 200 ms per request. 

            fetch("https://example.com/api/send", {"credentials":"include","body":`{\"xactionId\":\"${xId}\",\"symbol\":\"${xSym}\"}`,"method":"POST","mode":"cors"})
            .then(resp => resp.json())
            .then(json => {
            if(json.test.result === true) { 
                console.log(json.test.hash, json.test.number); 
                    resolve(false); //break
            }})
            .catch(err => console.log(err));

            resolve(true); //continue
          }, 200);
    if (!continue) break;
    })
  }
}

Basically you have different contexts in here, one of them is the loop, the other is the Promise (and the setTimeout callback). Returning from the setTimeout would not resolve your promise nor return you anything useful outside. What we do here is that we await the promise to resolve and we recover a boolean that tells us if we should break the loop. Then, in the context of the loop we decide to either break or continue.

Upvotes: -1

Related Questions