javascriptasynchronoussleepselectors-api

Making a bookmarklet to click all the 'more >' links on Word Hippo, doesn't wait between clicks


Here is what I have, that I've been running in the console (I've added linebreaks for readability):

let thingsToClick = []; 
// sleep function from https://stackoverflow.com/a/64762041
const sleep = t => new Promise(s => setTimeout(s, t)); 
do{
    (async () => {
        thingsToClick = $$('div.morewordsblock[onclick]:not([style])');
        console.log(thingsToClick.length);
        thingsToClick.forEach(async clickable => {
            console.log(clickable.id);
            clickable.click();
            // wait between clicks
            await sleep(5000);
        });
        console.log('waiting'); 
        // wait between rounds of clicking
        await sleep(5000);
    })();
} while (thingsToClick.length >=0);

The problem I have is that it's not actually waiting in either case, and just hammering the page, meaning it doesn't have time to respond between loops.

For completeness I've been running this on https://www.wordhippo.com/what-is/another-word-for/peach.html


Solution

  • Async in loops is fraught with issues

    It's much better to use recursion here, especially when the number of things looped over is small.

    Based on this answer here is a change in the code to use recursion:

    let thingsToClick = []; 
    // sleep function from https://stackoverflow.com/a/64762041
    const sleep = t => new Promise(s => setTimeout(s, t)); 
    const promises = [];
    const getThingsToClick = () => document.querySelectorAll('div.morewordsblock[onclick]:not([style])');
    const clickMore = async () => {
      thingsToClick = getThingsToClick();
      console.log(thingsToClick.length);
      promises.concat(Array.from(thingsToClick || []).map(async clickable => {
        console.log(clickable.id);
        clickable.click();
        // wait between clicks
        await sleep(500);
      }));
      await Promise.all(promises)
      console.log('waiting'); 
      // wait between rounds of clicking
      if(thingsToClick.length > 0) {
        await sleep(500);
        await clickMore();
      } else { console.log('finished') }
    };
    clickMore();