2

I am having a hard time with wait loops in JavaScript to wait for content from an external source (tasker).

In particular I have an HTML document with JavaScript (shown in a WebView element inside a Tasker (Android app) scene on my Android phone) which should show results from the Google Places API (new version). I am using Tasker to retrieve the data from Google which works quite well. I can also read the retrieved data with JavaScript. But I can't figure out how to build a wait loop with JS which checks if the data retrieval with Tasker has finished.

I've experimented with setInterval and setTimeout.

Below is one of my attempts. The wait for the value of the second control variable to equal the value of the first control variable works well, indicating that the data retrieval by Tasker has finished. The counter usually is around 6 at this point. However, then the content of the if (answer == random) condition is also executed 6 times (e.g. retrievedData is set 6 times) and the function this.fillHtmlElements() doesn't seem to be executed.

Everything works fine if I only set one setTimeout to 3 or 4s; but that would not account for variable internet speed etc.

loadContent(){
          const myInterval = setInterval(dataRetrieve, 500);
          let random = Math.random().toFixed(5); // this generates a random number as a control variable
          setGlobal('CheckNumberIn', random); // This function sets the global Tasker variable 'CheckNumberIn' to the control variable. When the Tasker loading Task has finished, Tasker will set the value of its second global control variable ('CheckNumberOut') to the same value
          performTask('loadingGoogle', '15', this.locationType, Data.distance); // this initiates the tasker task 'loadingGoogle' to retrieve the data; '15' is the priority of the Tasker task, this.locationType is the searched location Type (e.g. sushi_restaurant), Data. distance the search radius
          let counter = 0;
          function dataRetrieve() {
            let answer = global('CheckNumberOut'); //retrieves the value of the second control variable 'CheckNumberOut'
            if (answer == random) { //checks if value of second control variable equals the value of the first control variable -> meaning that Tasker has loaded the data from Google
                let retrievedData = global ('RetrievedData'); //reads the retrieved data from the global Tasker variable 'RetrievedData'
                this.inputData = JSON.parse(this.retrievedData); //parses the received JSON-data
                this.fillHtmlElements(); //function to update HTML elements with new received data
                clearInterval(myInterval);
            } else if (counter < 30) {
              counter++;
            } else {
              clearInterval(myInterval);
            }

          }
        }
2
  • The usual JavaScript way to wait for an async operation is to use a promise. Does performTasks return a promise? Commented Oct 13, 2024 at 4:14
  • Thank you very much for your comment. As far as I know, Tasker cannot actively communicate with the Webview element/ Javascript (only by reloading the page). So the only option is to check for changed Tasker variables. Commented Oct 15, 2024 at 19:13

1 Answer 1

1

If the performTask('loadingGoogle', '15', this.locationType, Data.distance) doesn't return a Promise (or invokes a callback), you would have to resort to polling.

I wrote this function and AI helped finalize.

function pollCondition(testFn, callbackFn, interval = 40, timeoutDuration = 0) {
  const startTime = Date.now();

  function checkCondition() {
    if (testFn()) {
      callbackFn();
    } else if (timeoutDuration && (Date.now() - startTime >= timeoutDuration)) {
      console.log(`Polling timed out after ${timeoutDuration}ms`);
    } else {
      setTimeout(checkCondition, interval);
    }
  }

  checkCondition();
}

// usage
var target = 12

var testFn = function() {
  return target == parseInt(Math.random() * 100)
}

var callbackFn = function() {
  console.log("found it")
}

console.log("waiting for random number to equal " + target)
pollCondition(testFn, callbackFn, 50, 3000)

A more modern version would be to make pollCondition return a Promise.

Let's use AI for this:

function pollCondition(testFn, interval = 40, timeoutDuration = 0) {
  return new Promise((resolve, reject) => {
    const startTime = Date.now();

    function checkCondition() {
      if (testFn()) {
        resolve();
      } else if (timeoutDuration && (Date.now() - startTime >= timeoutDuration)) {
        reject(new Error(`Polling timed out after ${timeoutDuration}ms`));
      } else {
        setTimeout(checkCondition, interval);
      }
    }

    checkCondition();
  });
}

// usage:
var target = 12

var testFn = function() {
  return target == parseInt(Math.random() * 100)
}

var callbackFn = function() {
  console.log("found it")
}

async function main() {
  console.log("waiting for random number to equal " + target)
  try {
    await pollCondition(testFn, 50, 3000)

    callbackFn()
  } catch (ex) {
    console.log("error: " + ex.message)

  }
}

 main();

So to finalize an answer:

function pollCondition(testFn, interval = 40, timeoutDuration = 0) {
  return new Promise((resolve, reject) => {
    const startTime = Date.now();

    function checkCondition() {
      if (testFn()) {
        resolve();
      } else if (timeoutDuration && (Date.now() - startTime >= timeoutDuration)) {
        reject(new Error(`Polling timed out after ${timeoutDuration}ms`));
      } else {
        setTimeout(checkCondition, interval);
      }
    }

    checkCondition();
  });
}


async function loadContent() {
  const wait = 500
  const timeout = 15000
  let random = Math.random().toFixed(5);
  setGlobal('CheckNumberIn', random);

  performTask('loadingGoogle', '15', this.locationType, Data.distance);

  const testFn = () => global('CheckNumberOut') === global('CheckNumberIn')

  try {
    await pollCondition(testFn, wait, timeout)
    let retrievedData = global('RetrievedData');
    this.inputData = JSON.parse(retrievedData);
    this.fillHtmlElements();
  } catch (ex) {
    console.log("error: " + ex.message)
  }

}

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much. This works ;-) I haven't understood every detail of the code, so I am going to experiment with it a little bit ;-)
If I call a function checking for the condition outside of the loop, it also works: loadContent = () => { ... function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function delayedLoop() { for (let i = 0; i < 30; i++) { let answer = global('CheckNumberOut'); loaderFunction(random, answer, i); if (answer == random){break} await delay(500); } } const loaderFunction = (random1, answer1, counter) => { if (random1 == answer1) { let RetrievedData = global ('RetrievedData'); ... } else { } } delayedLoop(); }

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.