1

i don't really understand async. i have a function like this:

function getTeam() {
    let sir = setInterval(() => {
        const teamsGrid = $('[class*="teamsgrid"]').find("p");
        const firstTeam = $(teamsGrid[0]).text();
        if (firstTeam != '') {
          clearInterval(sir)
          return firstTeam.trim()
        }
    }, 100)
}

im not js master. i just want to get that element when it loads in, this code is running in a userscript and // @run-at document-idle doesnt help either. i knew i would have to get into async js promises callbacks and whatever someday but i really dont understand how it works after pages of docs and other stackoverflow. when i console.log this function it will print undefined once then if i have a console.log inside the if it will print the actual team name. how do i wait for that result

  • Please share your HTML too. How is the `p` element filled with the value? – Noam Feb 09 '22 at 14:52
  • i dont think you are using it right, i can see you probably trying to return the `firstTeam.trim()` as the results of `getTeam()`, but you forgot there is a closure you used in `setInterval`, so the `firstTeam.trim()` is returned as the result of closure instead of `getTeam()` – 0nepeop1e Feb 09 '22 at 14:56
  • @Noam the html is from a random site. it has a div within a div which contains two p tags. i want to get the text of the first one. the class names of these elements are randomised the only constant being the teamsgrid thing. –  Feb 09 '22 at 14:57
  • @Onepeop1le yes ineed i used setinterval wrong, i used it because i originally wanted to do it async but i do not know how to retry the same function if a promise fails. –  Feb 09 '22 at 15:01
  • does the html will keep changing? if its static, what you need is just `$(document).ready(()=>{...})` or put the script tag after the element, then the element will guaranteed to be found, no need promise or `setInterval` or `setTimeout`, but first you need to clearly know what you actually want – 0nepeop1e Feb 09 '22 at 15:14

3 Answers3

1

Answer regarding the javascript language part if the question

You could modify your code to the following (but don't - see further below - I'm just providing this as your StackOverflow tags included async/await):

async function getTeam() {
    return new Promise(resolve => {
      const sir = setInterval(() => {
        const teamsGrid = $('[class*="teamsgrid"]').find("p");
        const firstTeam = $(teamsGrid[0]).text();
        if (firstTeam != '') {
          clearInterval(sir);
          resolve(firstTeam.trim());
        }
      }, 100);
    });
}

// ... and then anywhere else in your code:
doSomethingSynchronous();
const team = await getTeam();
soSomethingSynchronousWithTeam(team);

Note that this will only work with modern browsers supporting >= ECMAScript 2017: https://caniuse.com/async-functions (but luckily that's most by now!)

Answer regarding the implicit "howto wait for an element part"

... you really shouldn't actively wait for an element because this is unnecessarily heavy on the CPU. Usually you'll have some kind of event that informs you as soon as the element you're waiting for has been created. Just listen for that and then run your code.

What to do, if there's currently no such event:

lentschi
  • 300
  • 1
  • 11
0
function getTeam() {
    let sir = new Promise((res, rej) => {
    const teamsGrid = $('[class*="teamsgrid"]').find("p");
        const firstTeam = $(teamsGrid[0]).text();
        if (firstTeam != '') {
          clearInterval(sir);
          res(firstTeam.trim());
        }
    });
    return sir();
}

From what I understood, you are looking for firstTeam. Also, we assume that there is always firstTeam, so there isnt a case when there would be no team name. I am not sure where you are making a req that will take time to process honestly from this code. So far it looks that sync function should do just fine. Are you reaching out to any API?

DToxVanilla
  • 214
  • 2
  • 8
  • you forgot you should use `res(firstTeam.trim())` rather than return – 0nepeop1e Feb 09 '22 at 15:16
  • i am not making a request, this is a userscript and it loads when the specified site loads, there is always a fristTeam but if i execute your async function and console.log it out i get a promise, i dont get firstTeam value the promise thing you did was interesting but what if the if fails. what happens then –  Feb 09 '22 at 15:21
  • The `$` in OPs code is `jQuery`. It does not work in async and there is no point in doing `await` there. – Tomáš Zato Feb 09 '22 at 15:22
0

You can make it async if you want, but the main part us going to be using events instead. There is a special object called mutation observer. It will call a function you give it any time there's a change in the element you're observing.

Check the mutation observer docs to understand the code below: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

Without knowing much about your HTML, I can say as much as this should work:

function getTeam() {
  const teamsGrid = $('[class*="teamsgrid"]').find("p");
  const firstTeam = $(teamsGrid[0]).text();
  if (firstTeam != '') {
    return firstTeam.trim()
  }
}

function getTeamWhenAvailable() {
  // returning a promise allows you to do "await" and get result when it is available
  return new Promise((resolve, reject) => {
    // furst try if element is available right now
    const teamText = getTeam();
    if(teamText) {
      // resolve() "returns" the value to whoever is doing "await"
      resolve(teamText);
      // resolve does not terminate this function, we need to do that using return
      return;
    }
      

    // Mutation observer gives you list of stuff that changed, but we don't care, we just do our own thing
    const observer = new MutationObserver(()=>{
          const teamText = getTeam();
          if(teamText) {
            // stop observing
            observer.disconnect();
            // resolve the value
            resolve(teamText);
          }
    });
    observer.observe(document.body, { childList: true, subtree: true };
  })
}

// usage, the extra brackets around the lambda cause it to invoke immediatelly

(async () => {
  console.log("Waitinf for team...");
  const teamName = await getTeamWhenAvailable();
  console.log("Result team name: ", teamName)
})();

Now you might wanna narrow the scope of the mutation observer, in the example above it watches the entire document. Try to instead observe the deepest element that you can rely on not being removed.

If you need to receive team name multiple times, I think you should just go with the obsever alone without the async stuff.

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778