Dante
Dante

Reputation: 476

JavaScript wait for async functions in while loop

I currently have 5 functions; each one uses setInterval to wait for an element to load and than clicks the element when it is available which leads to the next webpage. These functions also take place inside of a while loop. It is very important that these functions take place one after another and the while loop waits for all the functions to complete before looping again. Due to the functions being asynchronous the loop will run x times before any of the functions can even load.

Example of what I am trying to do:

function one () {
    var checkForItem = setInterval(function () {
        if ($('#element').length) {
            $('#element').click();
            clearInterval(checkForItem);
        }
    }, 100);
}

Imagine 5 of these functions (one, two, three, four, five), all with the same format using setInterval and the following while loop:

var x = 0, y = 10;

while (x < y){
    one();
    two();
    three();
    four();
    five();
    x++
}

How would I go about ensuring all the functions take place one after another before having the loop continue?

Note: I have tried using promises although due to the functions being async the loop still continues before the functions complete.

Upvotes: 3

Views: 7956

Answers (3)

Hank Phung
Hank Phung

Reputation: 2149

Try wrap promise with async await:

async function one (){
      await (new Promise(function(reolve, reject){
         var checkForItem = setInterval(function () {
           if ($('#element').length) {
              $('#element').click();
              clearInterval(checkForItem);
              resolve();
            }
         }, 100);
      }));
}

//And then in your while loop:
while (x < y){
    await one();
    await two();
    ...
    x++
}

note: your while loop must be wrapped in an async function also.

Upvotes: -1

Bergi
Bergi

Reputation: 664206

Use async/await syntax with the promises:

function delay(t) {
    return new Promise(resolve => setTimeout(resolve, t));
}
async function one() {
    while (!$('#element').length) {
        await delay(100);
    }
    $('#element').click();
}

async function main() {
    for (var x = 0; x < 10; x++) {
        await one();
        await two();
        await three();
        await four();
        await five();
    }
}

Upvotes: 4

CertainPerformance
CertainPerformance

Reputation: 370639

Define the selectors in an array, and gradually iterate over the array in the interval, until the end of the array is reached:

const initialSelectorsToFind = ['#element1', '#element2', '#element3']; // add more as desired
const elementSelectorsToFind = Array.from({ length: 10 })
  .reduce(arrSoFar => [...arrSoFar, ...initialSelectorsToFind], []);
let elementIndexToFind = 0;
function tryClick(){
  const elementToFind = $(elementSelectorsToFind[elementIndexToFind]);
  if (elementToFind.length) {
    elementToFind.click();
    elementIndexToFind++;
    if (elementIndexToFind === elementSelectorsToFind.length) {
      clearInterval(tryClickInterval);
    }
  }
}
const tryClickInterval = setInterval(tryClick, 100);

But if you're trying to trigger a function (such as something that clicks an element) when an element gets added to the DOM, it would be far better off to use something that triggers when the add occurs, such as a callback in the creator function, or MutationObserver

Upvotes: 0

Related Questions