2
<script>
var fn = function(url){
    // bla bla bla...
};
</script>

<input type="button" onclick="fn()" value="Execute">
<input type="button" onclick="???" value="Abort">

Just like above, when a user click on the “Execute” button, the fn() function will be executed.

Suppose the fn() function will run for a very very long time, and if the user want to stop the execution of this function midway, how should I do?

Banana Code
  • 759
  • 1
  • 12
  • 28

5 Answers5

4

Functions in JavaScript are blocking -- which means that everything[1] freezes as the function runs. The screen doesn't redraw, mouse clicks are not processed, keyboard events are not processed.

Nothing happens until the function is done.

The only way to counter this is to use async coding to run your code in small, bite-sized chunks.

There are a number of tools to use, including:

I'd recommend looking at the async tools then asking another question on how to make your function non-blocking.

[1] For the purposes of this discussion

Community
  • 1
  • 1
Jeremy J Starcher
  • 23,369
  • 6
  • 54
  • 74
  • hi Jeremy! I can understand time out but can you please explain me on your concept of 'keeping array index to generator functions' a bit detailed? If you share links to go through, that would also be great. – Mohamed Idris May 22 '16 at 06:35
  • @MohamedIdris Updated the answer to be clearer. Sorry. – Jeremy J Starcher May 22 '16 at 06:41
0

You can set second input type="button" disabled attribute; create a variable referencing deferred object; remove disabled attribute at input type="button" when deferred object is created; call deferred.reject() with new Error() as parameter at click of second input type="button"

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<script>
var ajax, d;
function fn(event, url){
    // bla bla bla...
  ajax = new $.Deferred(function(dfd) {
    d = dfd;
    $("input[value=Abort]").removeAttr("disabled")
     setTimeout(function() {
       dfd.resolve("complete")
     }, Math.random() * 10000)
  });
  ajax.then(function(complete) {
    console.log(complete)
  }, function(e) {
       console.log(e)
  })
};
</script>

<input type="button" onclick="fn(event)" value="Execute">
<input type="button" onclick="d.reject(new Error('aborted'))" value="Abort" disabled>
guest271314
  • 1
  • 15
  • 104
  • 177
0

Generally not, you can't. At least I don't think you can. But you can and should write your functions in such way that they don't block your UI. UI and JavaScript are using one thread, so if very long function is running in JavaScript user cannot use UI. So click event would not be handled anyway. Instead what you want is to split long computation in parts using setTimeout (or some library that uses it internally). Then check for aborting condition. Very simplistic example of such thing is in snippet below.

var abort = false;
var target = document.getElementById('target');

function veryLongFunction(step) {
  step = step || 0;
  
  if (abort) {
    return;
  }
  
  target.innerHTML = target.innerHTML + '<br>' + step;
  
  window.setTimeout(veryLongFunction.bind(null, step + 1), 1000);
}

document
  .getElementById('start')
  .addEventListener('click', function () {
    veryLongFunction(0);
  });

document
  .getElementById('stop')
  .addEventListener('click', function () {
    abort = true;
  });
<button id="start">start</button>
<button id="stop">stop</button>
<div id="target"></div>
sielakos
  • 2,406
  • 11
  • 13
0

You can break the function into parts and stall the parts with a timer. A timer breaks up the event loop to allow something like a button click to update a variable, like setting abort = true.

In the broken up function, check if the variable is updated to indicate you should abort. Return if it is, otherwise keep running the function.

There are different ways to abort ajax requests. You can set up a pool of open requests and a function for your onclick event to abort them.

Something like this with jquery:

var ajaxPool = [];

$.ajaxSetup({
    beforeSend: function(jqXHR) {
        ajaxPool.push(jqXHR); // keep track of active requests
    },
    complete: function(jqXHR) {
        var index = ajaxPool.indexOf(jqXHR);
        if (index > -1) {
            ajaxPool.splice(index, 1); // remove completed requests
        }
    }
});

//the function to run onclick:

function abortAllAjax () {
    for (var i = 0; i < ajaxPool.length; i++) {
        ajaxPool[i].abort();
    }
    ajaxPool = [];
};
curtwphillips
  • 5,441
  • 1
  • 20
  • 19
-1

I'm pretty sure there's not, but if there would, it would be wrong.

In a single threaded language, a correct solution could be to check a stop flag after doing some work. If the flag (a property or a global variable) becomes true, the algorithm does whatever it has to do to stop and the function returns.

If the algorithm is long or contains a loop, it would look like this:

var running=true;
var fn = function(url){
    while(1) {
        if(!running) return;
        // bla bla bla...
    }
};

If you meant an ajax call that would take long to complete, you can abort it with abort():

var xhr=$.ajax(...);
...
xhr.abort();
Gabriel
  • 2,170
  • 1
  • 17
  • 20
  • In a single threaded JS engine, your flag can never be changed by other code as long as your `while` loop is still running. – jfriend00 May 22 '16 at 06:51
  • That is correct. I was thinking about some event, like a `click` event... but only if the loop does not kill the page. Anyways, it is bad code, thanks for the comment. – Gabriel May 22 '16 at 07:01