简体   繁体   English

Javascript forEach-在迭代之前等待setTimeout完成

[英]Javascript forEach - waiting for setTimeout to complete before iterating

I have the following: 我有以下内容:

https://jsfiddle.net/qofbvuvs/ https://jsfiddle.net/qofbvuvs/

var allItems = ["1", "2", "3"]
var allPeople = ["A", "B"]

var testFoo = function(itemValue, peopleValue) {
  setTimeout(function(){
      return itemValue == "3" && peopleValue == "B"
  }, 200)
}

allItems.forEach(function(itemValue) {
  allPeople.forEach(function(peopleValue) {
    // I want to iterate through each object, completing testFoo before moving on to the next object, without recursion.  TestFoo has a few instances of setTimeout.  I've tried using promises to no avail.
    if (testFoo(itemValue, peopleValue)){
        alert("success")
    } else{
        // nothing
    };
  })
})
alert("complete")

My goal is to iterate through each item, one by one, in order, while waiting for the results of testFoo . 我的目标是依次等待每个项目,同时等待testFoo的结果。 If testFoo passes, then I should stop execution. 如果testFoo通过,那么我应该停止执行。

I've tried to use promises ( https://jsfiddle.net/qofbvuvs/2/ ) but can't get the behavior I'm looking for. 我尝试使用Promise( https://jsfiddle.net/qofbvuvs/2/ ),但无法获得我想要的行为。 Success should be called before Complete . Success应该在Complete之前调用。 TestFoo has a couple setTimeouts that I need to work around (it's a library I can't modify). TestFoo有几个setTimeouts我需要解决(这是我无法修改的库)。 How can this be achieved? 如何做到这一点?

One way you can do it is through jQuery deferreds and manually stepping through your arrays, rather than using the built in loops, so you can control if/when you proceed. 做到这一点的一种方法是通过jQuery延迟并手动步进遍历数组,而不是使用内置循环,因此您可以控制是否/何时进行。 I guess it uses recursion in a way, but for nothing more than to invoke the next iteration - there is no crazy recursive return value unraveling or anything that makes recursion complex. 我猜想它以某种方式使用了递归,但无非就是调用下一个迭代-没有疯狂的递归返回值分解或使递归变得复杂的任何事情。 Let me know if this works for you: 让我知道这是否适合您:

 var allItems = ["1", "2", "3"] var allPeople = ["A", "B"] var testFoo = function(itemValue, peopleValue) { var deferredObject = $.Deferred(); setTimeout(function() { deferredObject.resolve(itemValue == "3" && peopleValue == "B") }, 200) return deferredObject; } var currentItemIndex = 0; var currentPeopleIndex = 0; var testDeferred = $.Deferred(); function testAll() { testFoo(allItems[currentItemIndex], allPeople[currentPeopleIndex]).done(function(result) { if (result) { // found result - stop execution testDeferred.resolve("success"); } else { currentPeopleIndex++; if (currentPeopleIndex >= allPeople.length) { currentPeopleIndex = 0; currentItemIndex++; } if (currentItemIndex >= allItems.length) { // result not found - stop execution testDeferred.resolve("fail"); } else { // check next value pair testAll(); } } }); return testDeferred; } testAll().done(function resolveCallback (message) { alert(message); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

Since there's already a bunch of solutions out, I figured i'd add a solution using async/await . 由于已经有了很多解决方案,所以我想我会使用async/await添加一个解决方案。 I personally like this one because it keeps the code similar to your original and more compact, where you don't have to keep track of any intermediate states or resolve all promises. 我个人喜欢这一代码,因为它使代码与您的原始代码相似且更加紧凑,在此您无需跟踪任何中间状态或解决所有承诺。 The solution is just to wrap the for loops inside an async function and change the forEach to a basic for(var x...) because await wont work correctly within a forEach statement because you are defining an inner non async function and calling await within that is invalid. 解决方案是将for循环包装在异步函数中,并将forEach更改为基本的for(var x ...),因为await在forEach语句中无法正常工作,因为您正在定义内部的非异步函数并await内部调用await那是无效的。 Additionally change the testFoo function to return a promise. 另外,更改testFoo函数以返回承诺。

Since the for loops are inside a function you an easily exit it once a match has been found to skip further checks. 由于for循环位于函数内部,因此一旦找到匹配项就可以轻松退出该函数,以跳过进一步的检查。 I also added a return statement at the end of the loops to indicate nothing has been found, which can be handy. 我还在循环末尾添加了return语句,以指示未找到任何内容,这很方便。

Finally, since async functions themselves return a promise, all you have to do is add the final alerts inside the then of the returned promise to evaluate if a match has been found or not. 最后,由于异步函数本身返回一个承诺,所有你需要做的就是里面添加的最后警告then返回的承诺,如果发现匹配与否来评价。

async/await example : 异步/等待示例

 var allItems = ["1", "2", "3"] var allPeople = ["A", "B"] var testFoo = function(itemValue, peopleValue) { return new Promise(function(resolve, reject){ setTimeout(function(){ resolve(itemValue == "3" && peopleValue == "B"); }, 200) }); } async function checkItemPeople(){ for(var itm=0; itm<allItems.length; itm++){ for(var ppl=0; ppl<allPeople.length; ppl++){ var result = await testFoo(allItems[itm], allPeople[ppl]); if (result) { alert("success"); return true; } else { // nothing }; } } return false; } checkItemPeople().then(function(resp){ resp ? alert("Found Something") : alert("Theres nothing"); }); 

Used Promise.all to solve your problem. Promise.all解决您的问题。 Hope this helps. 希望这可以帮助。

 var allItems = ["1", "2", "3"]; var allPeople = ["A", "B"]; var testFoo = function(itemValue, peopleValue) { return new Promise(function(resolve) { setTimeout(function() { resolve(itemValue === "3" && peopleValue === "B"); }, 200); }); } var promises = []; allItems.forEach(function(itemValue) { allPeople.forEach(function(peopleValue) { promises.push(testFoo(itemValue, peopleValue).then(function(result) { console.log(result?'success':'fail'); return result; })); }) }); Promise.all(promises).then(function(results) { console.log('Result from all promise: ' + results); console.log("complete"); }); 

If you want to construct a chain of promises in such a way where each promise isn't created until it's predecessor promise in the chain resolves, you should create an array of functions that return your promises and use reduction to create the needed promise chain. 如果您要以这样一种方式构造一个承诺链,即在链中的前一个承诺被解决之前才创建每个承诺,则应创建一个函数数组,这些函数返回您的承诺并使用约简来创建所需的承诺链。

 var allItems = ["1", "2", "3"]; var allPeople = ["A", "B"]; // Async requests aren't made once we reach "2" and "B" var resultToStopOn = function(itemValue, peopleValue) { return itemValue === "2" && peopleValue === "B" } var testFoo = function(itemValue, peopleValue) { return new Promise(function(resolve) { console.log('Starting promise for: ', itemValue, peopleValue); setTimeout(function() { console.log('Resolving promise for: ', itemValue, peopleValue); resolve(resultToStopOn(itemValue, peopleValue)); }, 200); }); } var functionsThatReturnPromises = []; allItems.forEach(function(itemValue) { allPeople.forEach(function(peopleValue) { functionsThatReturnPromises.push(function() { return testFoo(itemValue, peopleValue); }); }); }); functionsThatReturnPromises .reduce(function(chainOfPromises, fnReturningAPromise) { return chainOfPromises.then(function(result) { // if result is false, we continue down the chain // otherwise we just propagate the result and don't chain anymore return !result ? fnReturningAPromise() : result; }); }, Promise.resolve()) .then((result) => { console.log('Result: ', result); }); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM