3

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.

5
  • Indenting your code properly would make it a lot more readable. Commented Apr 15, 2018 at 0:07
  • What is the reason for executing the loop if it's not modifying the function's behavior? by executing those functions once will work Commented Apr 15, 2018 at 0:24
  • @JoseAPL The functions will act differently as x increases. The above is just a simple example of what I am trying to do. The functions will click different buttons and scrape some content based on an array and the x value. Commented Apr 15, 2018 at 0:42
  • 1
    Each one of your functions needs to return a promise so you can wait for all the calls using Promise.all Alternatively, you can use github.com/angular/zone.js which does track asynchronous JS code by patching low level calls like setTimeout Commented Apr 15, 2018 at 1:00
  • 2
    each one uses setInterval to wait for an element to load - sounds hackish Commented Apr 15, 2018 at 3:38

3 Answers 3

4

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();
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

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

2 Comments

First off, I want to thank you as you also helped me last night! This solution won't work for me as I essentially need elementSelectorsToFind to be traversed x times, hence the need of a loop.
See edit: you can use .reduce at first to repeat the initial selectors any number of times
-1

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.

1 Comment

what's with the do nothing .then() ? Also, and it's not clear in your code at all, await one() etc must be performed in an async function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.