javascriptloopssettimeoutsetintervaltasker

Javascript loop to wait for input from external source (Android tasker)


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);
            }

          }
        }

Solution

  • 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)
      }
    
    }