3

This is an extension of my previous question, mainly out of curiosity: Kill all running JavaScript functions with Timeouts on click

I have a webpage with 3 buttons and a "result" section:

<button id="b1" onclick="f1()">1st button</button>
<button id="b2" onclick="f2()">2nd button</button>
<button id="b3" onclick="f3()">3rd button</button>
<p id="result">"Here will be the result!"</p>

Here are the snippets for the JavaScript functions. I also utilized web worker:

// The functions are similar, but the outputs are different

var worker;

function calcF1() {
    var answer;
    for (let i = 0; i < 1e9; i++) {
        // Do some complicated calculations
    }
    postMessage(answer);
}

function f1() {
    // TODO: Kill other running instances of all functions

    // Calling worker
    var cf1 = calcF1.toString();
    var code = cf1.substring(cf1.indexOf('{')+1, cf1.lastIndexOf('}'));
    var blob = new Blob([code], {type: 'application/javascript'});
    worker = new Worker(URL.createObjectURL(blob));

    worker.onmessage = message => {
        document.getElementById("result").innerHTML = "1st function";
    };
}

function calcF2() {
    var answer;
    for (let i = 0; i < 1e9; i++) {
        // Do some complicated calculations
    }
    postMessage(answer);
}

function f2() {
    // TODO: Kill other running instances of all functions

    // Calling worker
    var cf2 = calcF2.toString();
    var code = cf2.substring(cf2.indexOf('{')+1, cf2.lastIndexOf('}'));
    var blob = new Blob([code], {type: 'application/javascript'});
    worker = new Worker(URL.createObjectURL(blob));

    worker.onmessage = message => {
        document.getElementById("result").innerHTML = "2nd function";
    };
}

function calcF3() {
    var answer;
    for (let i = 0; i < 1e9; i++) {
        // Do some complicated calculations
    }
    postMessage(answer);
}

function f3() {
    // TODO: Kill other running instances of all functions

    // Calling worker
    var cf3 = calcF3.toString();
    var code = cf3.substring(cf3.indexOf('{')+1, cf3.lastIndexOf('}'));
    var blob = new Blob([code], {type: 'application/javascript'});
    worker = new Worker(URL.createObjectURL(blob));

    worker.onmessage = message => {
        document.getElementById("result").innerHTML = "3rd function";
    };
}

However, if the user clicks b1 and then immediately click b2, the worker by f1 will not terminate and keep running. If worker from f1 finishes before that from f2 does, then result will show 1st function and then replaced by 2nd function. There's also a possibility that the worker by f2 finishes first, in which case result would show 2nd function and then be replaced by 1st function.

What I want is to implement, at the start of each function, a statement that kills/ignores/bypasses all functions and/or all web workers that are currently running so that the final output would always be the one associated with the last button the user pressed. How can it be best implemented?

EDIT: Thanks for pointing out the web worker. My original implementations involved web workers but I forgot to mention. The question has been updated.

6
  • 1
    You cannot stop a for loop from other function since for loop is synchronized. You cannot even start f2 while f1 is running. Note that Javascript is running only in one thread unless you use web workers. Commented May 3, 2018 at 3:23
  • Possible duplicate of Kill all running JavaScript functions with Timeouts on click Commented May 3, 2018 at 3:29
  • It sounds like you want to use a web worker. Commented May 3, 2018 at 4:27
  • That was not a duplicate. For that question I asked for how to kill functions with setTimeout(), which seem to be easier to kill. @RuChernChong Commented May 3, 2018 at 6:31
  • Yes I was actually using one. I've updated the question to reflect that. @PatrickRoberts Commented May 3, 2018 at 6:33

1 Answer 1

1

Split up the functions such that they only run for a moderate period of time before stopping and pushing the next run onto the end of the stack with setTimeout, so that other functions have a chance to run. At the beginning of each f1, f2, and f3, check to see that they have the unique reference to the key of the current running function, and return if they don't.

In the following snippet, an ongoing f1 function is getting overridden by a more recent call of f2:

let runningFunctionKey;
function f1(key, start = 0) {
  if (!key) {
    runningFunctionKey = {}; // some unique reference
    key = runningFunctionKey;
  } else if (key !== runningFunctionKey) return;
  const until = start + 1e7;
  while (start < until) {
    if (start === 1e9) {
      console.log('f1 done');
      return;
    }
    start++;
  }
  console.log('f1 setting timeout');
  setTimeout(f1, 0, key, start);
}
function f2(key, start = 0) {
  if (!key) {
    runningFunctionKey = {}; // some unique reference
    key = runningFunctionKey;
  } else if (key !== runningFunctionKey) return;
  const until = start + 1e7;
  while (start < until) {
    if (start === 1e9) {
      console.log('f2 done');
      return;
    }
    start++;
  }
  console.log('f2 setting timeout');
  setTimeout(f2, 0, key, start);
}
f1();
setTimeout(f2, 500);

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

3 Comments

Symbol() would probably be a better candidate for a lightweight unique key to use, but I think that web workers would be the way to go here.
Nice answer! It does seem a little bit not efficient when you have to check at regular intervals, but definitely, this would work!
@Mushinako If the re-checking frequency is an issue, feel free to increase the execution duration (eg 1e8 rather than the current until = start + 1e7), though given this implementation, more frequent re-checking will make for a quicker response to user action.

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.