1

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!")
});
cigol on
  • 337
  • 3
  • 12
  • My friend, I think you miss understand a little bit the asynchronous programming paradigm! I suggest you revisit some of the tutorials that give deep explanation on how async/await works. – mytuny Apr 01 '19 at 20:47

2 Answers2

2

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;
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Hi @Bergi, thanks for the awesome response! 1 issue, wouldn't the await in front of the fetch and resp.json lines make it so that it would have to wait for the responses from each fetch request before sending the next request? I was using the promise as a way to delay the requests only. and then the fetch does not need to be resolved yet before the script should continue. I need to send the requests at a steady pace every 200 ms regardless of if the server has responded or not. and only when a response = true to have it break the loop. – cigol on Apr 01 '19 at 21:03
  • @GnGn Yes, doing `await delay(…); await fetch(…)` runs everything sequentially. To speed it up, see the other two snippets. – Bergi Apr 01 '19 at 21:53
  • 2
    The last example can also be achieved using `await`. https://gist.github.com/3limin4t0r/6e2e493e40079ae38561863079f68e1e – 3limin4t0r Apr 01 '19 at 22:23
-1

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.

Tiagojdferreira
  • 1,122
  • 2
  • 14
  • 20