Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wait until condition is met or timeout is passed in javascript

I need to sleep the code until some condition is met or a 3 second timeout is passed. then return a simple string. Is there anyway I can do this?

// this function needs to return a simple string 

function something() { 

    var conditionOk = false;

    var jobWillBeDoneInNMiliseconds = Math.floor(Math.random() * 10000);

    setTimeout(function() {

        // I need to do something here, but I don't know how long it takes
        conditionOk = true; 

    }, jobWillBeDoneInNMiliseconds);


    // I need to stop right here until
    // stop here until ( 3000 timeout is passed ) or ( conditionOk == true )
    StopHereUntil( conditionOk, 3000 );

    return "returned something"; 
}

here is what I exactly going to do:

I make the browser scroll to bottom of the page, then some ajax function will be called to fetch the comments (that I have not control on it). Now I need to wait until comments are appeared in document with ".comment" class.

I need the getComments() function return comments as a json string.

function getComments() {

    window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);

  var a = (document.querySelectorAll('div.comment'))

  // wait here until  (  a.length > 0  ) or ( 3 second is passed )

  // then I need to collect comments
  var comments = [];
  document.querySelectorAll('div.comment p')
    .forEach(function(el){      
        comments.push(el.text());
    });

  return JSON.stringify(comments);
} 

getComments();
like image 946
mdaliyan Avatar asked Jul 16 '26 09:07

mdaliyan


2 Answers

I came across this problem and none of the solutions were satisfactory. I needed to wait until a certain element appeared in the DOM. So I took hedgehog125's answer and improved it to my needs. I think this answers the original question.

const sleepUntil = async (f, timeoutMs) => {
    return new Promise((resolve, reject) => {
        const timeWas = new Date();
        const wait = setInterval(function() {
            if (f()) {
                console.log("resolved after", new Date() - timeWas, "ms");
                clearInterval(wait);
                resolve();
            } else if (new Date() - timeWas > timeoutMs) { // Timeout
                console.log("rejected after", new Date() - timeWas, "ms");
                clearInterval(wait);
                reject();
            }
        }, 20);
    });
}

Usage (async/await promise):

try {
    await sleepUntil(() => document.querySelector('.my-selector'), 5000);
    // ready
} catch {
    // timeout
}

Usage (.then promise):

sleepUntil(() => document.querySelector('.my-selector'), 5000)
    .then(() => {
        // ready
    }).catch(() => {
        // timeout
    });
like image 184
pbotas Avatar answered Jul 18 '26 23:07

pbotas


You should be able to achieve this using Promise.race. Here's a basic example:

let promise1 = new Promise(resolve => {
  setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise(resolve => {
  setTimeout(resolve, 800, 'two');
});

async function fetchAndLogResult() {
  let result = await Promise.race([promise1, promise2]);
  console.log(result);
}

fetchAndLogResult();

Here's an alternative version, more concise although not using async/await:

let promise1 = new Promise(resolve => {
  setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise(resolve => {
  setTimeout(resolve, 800, 'two');
});

Promise.race([promise1, promise2]).then(result => console.log(result));
like image 32
Jeto Avatar answered Jul 18 '26 21:07

Jeto



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!