已解决:如何根据 promise 的响应打破 for 循环?

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

所以我正在编写一个脚本来在站点上自动为我执行一些操作。到目前为止,我已经解决了所有问题,但我做错了一些事情,只是想找出更好的方法。

我有一个 for 循环,它将通过获取将请求发送到 API。循环次数将基于一个变量。我希望能够在每次提取时处理它们的响应,如果它们中的任何一个以 true 的结果出现,我希望它停止我的 for 循环继续。目前我的工作方式是刷新页面(这将停止我的循环)但这是非常低效的。我只需要以某种方式完成它。

至于 synchronous/asynchronous 我希望它发送请求而不考虑响应,直到响应为真然后我希望它停止发送请求。如果我有一些请求是可以的,这就是我的页面刷新方法现在发生的情况。

我乐于接受建议并改进整个事情(我只是边走边学,甚至不知道 2 个月前的 js 是什么。但是对于我已经想出的所有事情,我似乎无法掌握这个概念。)

我试过命名循环并中断@name,尝试返回一个中断,但我尝试过的都没有成功。唯一有的是页面刷新,我猜这是停止循环的一种方法。

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

以防万一有人需要我从服务器返回的响应。

{
  "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"
    }
  }
}

我希望能够停止 for 循环,在基于第二个结果的循环异步函数中。然后在获取 POST 请求之后。

此外,我想知道上述是否可行,同时在其自己的外部函数中也有获取请求,以便我也可以从代码中的其他地方调用它。


解决方案:我使用了@Bergi 建议的最后一个选项。感谢您的帮助!

我的最终代码如下所示:

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

避开Promise constructor antipattern!你应该使用

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

并且不要在其他地方使用 Promise 构造函数,尤其是环绕其他 promise 调用。我猜你正在寻找

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

或者可以同时使用 delay(200)fetch 运行,这样您至少要等待 200 毫秒,而不是在提取所花费的时间之外:

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

如果你真的每200毫秒一个获取请求,你不能在这里使用await。您必须在循环条件中使用布尔变量来检查是否有任何已收到的响应希望循环停止:

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

在你的 looper 中你有循环并且你做了一个 await:

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

Await 可用于将承诺的结果分配给变量。这样你就可以在承诺之外控制你的循环。

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

基本上这里有不同的上下文,其中一个是循环,另一个是 Promise(和 setTimeout 回调)。从 setTimeout 返回不会解决你的承诺,也不会 return 你在外面有任何用处。我们在这里所做的是我们等待解决的承诺,我们恢复一个布尔值,告诉我们是否应该打破循环。然后,在循环的上下文中,我们决定中断或继续。