-2

There are already a few javascript wait until questions on stackoverflow. But none of them address a wrapper function.

Could anyone provide wait until functionality and then wrap it in a function like:

WaitUntilThen(condition,functiontocall);

for example:

WaitUntilThen($('#myid').css('display')=='none',PrintCalculations());
6
  • 1
    What are you expecting condition to be? An observable? A promise? A literal condition wouldn't work since that would be evaluated immediately. Commented Feb 22 at 20:08
  • 1
    If the code is synchronous, you just need CalculationsDone(); PrintCalculations(); if the first function returns a promise CalculationsDone(),then(() => PrintCalculations()). If it doesn't return a promise then figure it out - maybe it takes a callback. If it doesn't, then you're out of luck. It's not at all clear what you're trying to do and what problem you're encountering that's not already solved. Commented Feb 22 at 20:11
  • I edited the question to clarify it with a real example. Commented Feb 22 at 20:12
  • Regarding your edit: you can't do that. == will evaluate immediately. You would need to, at the very least, wrap the condition in a function. Then, WaitUntilThen would need to poll the function repeatedly until it returned true. Or make the condition a promise, in which case the solution is just then. Commented Feb 22 at 20:13
  • It would be ok to get a "can't be done" answer. Even better would be working code with the wrapper function on top. Commented Feb 22 at 20:14

4 Answers 4

2

This can't be done literally. Arguments are evaluated before the function is called, so WaitUntilThen would just receive either true or false; depending on what the condition initially evaluated to.

Interpreting this question as literally as possible, I think you'd need to wrap the condition in a function, then have WaitUntilThen poll the function. Something kind of like:

function WaitUntilThen(conditionF, thenF, pollIntervalMs = 500) {
    const timer = setInterval(() => {
        const result = conditionF();
        if (result) {
            clearInterval(timer);
            thenF();
        }
    }, pollIntervalMs)
}

WaitUntilThen(() => $('#myid').css('display')=='none', PrintCalculations);

This is a terrible solution though. I would simply try to change how you're approaching the problem.


I was going to post a implementation closer to Pedro's answer, since returning a promise allows you to properly sequence code. It felt off though since if you're returning a promise, there's no need for thenF, since the caller could just use await/.then on the promise to do whatever they want after. If you're willing to abandon the callback argument, I'd go with what Pedro wrote, but just expect the caller to call PrintCalculations

function WaitUntilThen(conditionF, pollIntervalMs = 500) {
    return new Promise((resolve) => {
        const timer = setInterval(() => {
            const result = conditionF();
            if (result) {
                clearInterval(timer);
                resolve();
            }
        }, pollIntervalMs);
    });
}

(async () => {
    await WaitUntilThen(() => $('#myid').css('display')=='none');
    PrintCalculations()
});
Sign up to request clarification or add additional context in comments.

6 Comments

No matter how terrible you might think the answer is, this is useful functionality that is sadly missing from JS.
@Cymro I think you're trying to force a wrong way of thinking tbh. I'd go at this from another angle and either have the code that's changing the display also trigger PrintCalculations, or have that code emit some event that listeners could subscribe to. I think this way of approaching the problem only makes sense in cases where you don't have any control over the code (Userscripts to alter pages written by someone else).
The display thing was just a concrete example. This is really very useful functionality that maybe one day will be added to JS.
@Cymro Pedro's way is closer to what I originally was going to post, but it felt off as I wrote it. Returning a promise allows you to wait to allow code to be sequenced properly, but then that entirely defeated the purpose of callback/thenF. The function at that point should just poll the condition then resolve, and then the caller could do whatever they want after that point. I posted the code I did because it was closer to your literal question.
@Cymro See my addition.
|
2
function WaitUntilThen(condition, callback) {
    return new Promise(resolve => {
        const intervalId = setInterval(() => {
            if (condition()) {
                clearInterval(intervalId);
                resolve();
            }
        }, 100); 
    }).then(callback);

}

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
1

Your example calling code evaluates both the condition and the dependent function only once, and also does so immediately, without waiting.

If you are able to change the calling code to this:

WaitUntilThen(
    () => $('#myid').css('display') == 'none', // Condition function
    PrintCalculations // Function reference
);

Then those problems are gone, and this WaitUntilThen function will evaluate them in the way you desire:

function WaitUntilThen(fnCondition, fnToCall) {
    const checkCondition = () => {
        if (fnCondition())
            fnToCall();
        else
            setTimeout(checkCondition, 100);
    };
    checkCondition();
}

It uses a fixed interval of 100 ms, if needed you could make that value a parameter as well.

3 Comments

Peter, is there any way to achieve this with promises? fn().then( ()=>{Dosomething;} );
It would be better to "Ask" this as a new and separate question, because it actually is a new and separate question...
Ok, will do. Appreciate your help.
1

There are much better ways to achieve what you want but here is a function that accepts functions, booleans and promises as conditions and only calls back the function once the condition is satisfied:

function waitUntil(condition, callback, interval = 100) {
  const tryMeetingCondition = (result) => {
    if (result === true) {
      callback(result);
      clearInterval(intervalId);
    }
  }

  const checkCondition = () => {
    if (condition instanceof Promise) {
      condition.then(tryMeetingCondition);
    } else if (typeof condition === "function") {
      const result = condition();
      if (result instanceof Promise) {
        result.then(tryMeetingCondition);  
      } else {
        tryMeetingCondition(result);  
      }
    } else {
      tryMeetingCondition(condition);  
    }
  }

  const intervalId = setInterval(checkCondition, interval);
}

How to use:

const callback = () => {
  console.log("Called back");
}

const conditionOne = Promise.resolve(true);
waitUntil(conditionOne, callback);

const conditionTwo = () => {
  let condition = false;
  
  return () => {  
    if (!condition) {
      condition = true;
      return Promise.resolve(false); 
    }
    
    return Promise.resolve(condition);
  }
};
waitUntil(conditionTwo(), callback);

const conditionThree = () => {
  let color = 'red';
  let startTime = null;

  return () => {
    if (!startTime) {
      startTime = Date.now();
    }

    const elapsedTime = (Date.now() - startTime) / 1000; 

    if (elapsedTime >= 10) {
      color = 'blue';
    }

    return color === 'blue'; 
  };
};
waitUntil(conditionThree(), callback);

const conditionFour = () => true;
waitUntil(conditionFour, callback);

2 Comments

Ulmair I found your answer too difficult for me to understand.
@Cymro If you have any specific questions for this, please feel free to ask. I am more than happy to explain.

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.