This question already has answers here:
Implementing a pause and resume mechanism for javascript loop execution
(2 answers)
Closed 7 years ago.
I have a start button that when clicked runs a function that loops. How do I get a stopBTN.onClick to stop the running loop?
https://jsfiddle.net/vduxbnkj/
startBTN.onClick = function(){ runLoop(); }
function runLoop(){
while(condition true){
getFolderContentsLoop();
}
}
function getFolderContentsLoop(){
//loop through folders & files looking for .txt file and if "finished"
delete files and folders
}
If you're running a simple for (..) loop, this cannot be stopped via external influence. Everything is happening on the same thread in Javascript, unless your code "ends" at some point and returns control to the browser for a while no UI interaction can happen. The easiest way to have a "loop" is via a setTimeout or setInterval:
interval = null;
startBTN.onclick = function () {
var i = 0;
interval = setInterval(function () {
console.log(i++); // this is inside your loop
}, 1);
};
stopBTN.onclick = function () {
clearInterval(interval);
};
Javascript is single threaded and as long it is in a loop, it can't give control to other code to stop it. But if you have a special kind of loop that is implemented with setTimeout:
function loopStep() {
...
}
function loop() {
loopStep();
setTimeout(loop, 0);
}
then you can add a flag to be able to stop loop's execution:
var flag = true;
function loop() {
if (!flag) return;
loopStep();
setTimeout(loop, 0);
}
and then you can define your stop function:
function stop() {
flag = false;
}
I usually work around this by making my own boolean test as the while condition, like so:
var keepLooping = false;
while(!keepLooping){
document.getElementById("loopButton").onclick = function(){
keepLooping = true;
}
}
while(keepLooping){
//do something here
document.getElementById("loopButton").onclick = function(){
keepLooping = false;
}
}
The only method I can think of is to create a boolean at the very beginning, and set stopBTN.onclick as a function that switches the variable. Then put an if condition that uses break if the boolean is switched.
var r = false;
startBTN.onClick = function(){ runLoop(); }
stopBTN.onClick = function(){r = true; }
function runLoop(){
while(condition true){
getFolderContentsLoop();
if(r){
break;
}
}
}
function getFolderContentsLoop(){
/*loop through folders & files looking for .txt file and if "finished"
delete files and folders*/
}
It's crude, but it should work.
Related
This is an small snipped of a game developed in javascript.
It has a main function that loops infinitely.
PROBLEM
I want a single alert to show up when the character dies, otherwise it appears thousands of alerts due to the loop
characterDead = false;
function colision(){
//if colision > true
charactedDead = true;
alert("the character died")
}
function main(){
//...other functions
colision();
requestAnimationFrame(main);
}
window.onload = function() {main();};
You need a characterDead check in colision().
Also, you have a typo - "charactedDead" should be "characterDead" in the same function :)
var characterDead = false;
function colision() {
if (!characterDead) { // check if the character is dead already
characterDead = true; // kill it
console.log("the character died"); // alert or log this
}
}
function main() {
//...other functions
colision();
requestAnimationFrame(main);
}
window.onload = function() {
main();
};
I have an application using jquery that iterates through a series of checkboxes.
while it iterates through, I have a dialog showing the status of the printing session. I also have a cancel button to cancel the session.
<input type="button" id="cancelButton" onclick="cancel()">
function cancel() {
stop = true;
}
Through each iteration I check if stop is true and if it is, I break out of the loop. The problem however is that if I press the cancel button during the session, the cancel function will only run after the .each loop (kind of defeating the purpose). Is there a way to make the button more responsive or a better way to approach this?
Run the functions in separate threads?
setTimeout(function() { $('[name=check]:checked').each(function() {
printingFunction();
}); }, 1);
You could chain setTimeouts in order to make it possible to check for a Stop. You'd still have to wait for the current operation to complete, but it would let you break out of the loop.
function next()
{
printingFunction();
if (!stop && someOtherCheck)
setTimeout(function() { next(); }, 1);
}
next();
I don't think you can interrupt the .each loop as JavaScript execution is single threaded.
However I think timers are executed asynchronously, you might try something like below to cancel and clear.
Something like the logic below may get you there.
var timeouts = [];
$.each(someArray, function(index, value){
timeouts.push(setTimeout(function(){
console.log(index);
},5000*index));
});
$('#cancel-button').click(function(){
$.each(timeouts, function (_, id) {
clearTimeout(id);
});
timeouts = [];
}):
Also, you may consider playing with .index() if the above doesn't help.
Try it again - I made a change to the shift statement that will clear the error in IE 11:
var ele = Array.prototype.shift.call($checked);
You need to use setTimeout on the each loop call (can be set to 0) so the cancel function gets a chance to execute - something like this:
function cancel() {
stop = true;
}
function printingFunction() {
var x = 0;
while (x < 10000){
x+=1;
}
}
function printLoop($checked) {
if (stop) {
console.log('Stopped - button pushed')
} else {
if ($checked.length > 0) {
var ele = Array.prototype.shift.call($checked);
printingFunction()
}
if ($checked.length > 0) {
setTimeout(function(){printLoop($checked)}, 0);
} else {
console.log('Stopped - array depleted')
}
}
}
var stop = false,
$checked = $('[name=check]:checked');
printLoop($checked)
I have a loop that runs indefinitely until I tell it to stop. I am actually using requestAnimationFrame and a lot more is going on, but the below example is just to simplify my question.
var _stop = false;
var loop = function () {
while (!_stop) {
if (some condition is met) {
stop();
}
/* Do something. */
loop();
}
};
function stop() {
_stop = true;
}
Now this all works great, but it will still run /* Do something */ one more time before it actually stops. I want it to stop immediately and return.
Of course this can be done like so:
if (some condition is met) {
stop();
return;
}
But is there a way to include the return part into stop();? This doesn't do what I want for obvious reasons:
function stop() {
_stop = true;
return;
}
But is there a way to achieve this?
var _stop = false;
try {
var loop = function () {
if(!_stop) {
if (some condition is met) {
stop();
}
/* Do something. */
loop();
}
};
} catch(e) {
}
function stop() {
_stop = true;
throw new Error("USE IT WITH PRECAUTION");
}
The loop above does you job of exiting entire loop, But I will say its horribly wrong way of doing thing as ideally function should be
1) mutating the state variables
2) or should be computing the values.
3) or should be determining error state to stop the execution further
It should never be bothered about how the control flow of function caller is and ways to stop function caller execution flow.
It sounds like you want to check the condition (before) every time the work is done. To do this with a _stop variable (as opposed to simply checking the condition in the while condition itself), you have to:
Set the variable based on the condition before starting the loop
Do your work
Set the variable based on the condition before the next loop iteration
Whether you accomplish this with a while() loop or a do while() loop, the process will be the same. Adding a pre-loop check to your example will prevent the work from being done if the user has already exited:
var _stop = false;
// Call loop() when required
var loop = function () {
// Check condition before first iteration
if (/* some condition is met */) {
stop();
}
while (!_stop) {
/* Do your work */
// Check condition before every subsequent iteration
if (/* some condition is met */) {
stop();
}
loop();
}
};
function stop() {
_stop = true;
}
Is there a reason you are recursively calling loop() instead of calling it once and doing all of your work within the contained loop until the user exits? This more simplified version might work for you:
var _stop = false; // Set it initially, could also use checkStopRequired() here
// Call loop() when required
var loop = function () {
// Check condition before first iteration
_stop = checkStopRequired();
while (!_stop) {
// Do your work, setting _stop to true if work returns early
_stop = !doMyWork();
// Check condition before every subsequent iteration
_stop = checkStopRequired();
}
};
function checkStopRequired() {
// Return true if should stop, false if should continue
}
When doing the work required for each loop iteration, you may want to check the exit condition before any expensive operations to allow the whole thing to halt as soon as an exit condition is met, as opposed to waiting for the work to finish. This obviously depends on what work you're doing and what the exit conditions are.
An example of the function to be called within the loop, which will help you set _stop if the stop condition is met part-way through:
function doMyWork() {
// Get user input here...
if (checkStopRequired()) { return false; }
// Get data here...
if (checkStopRequired()) { return false; }
// Do logic here...
if (checkStopRequired()) { return false; }
// Render objects here...
// Return successful result
return true;
}
it might not be the optimal way to do this, but this can be done like this:
var _stop = false;
var flag=0;
var loop = function () {
if(!_stop) {
if(flag){
return;
}
if (some condition is met) {
stop();
_stop = true;
loop();
}
/* Do something. */
loop();
}
};
function stop() {
//_stop = true;
toReturn();
}
function toReturn(){
flag=1;
}
I have a function which accepts a string and outputs it one character at a time with a delay. The event occurs when the user clicks a link or button. The problem is that if the user clicks a link and then another before the first one is done, they both run at the same time and output to the screen. It becomes jumbled up.
ex:
string1 : "i like pie very much"
string1 : "so does the next guy"
output : i sloi kdeo epse .... and so on.
Anyone know a method to fix this?
I think I need a way to check if the function is being processed already, then wait till it is done before starting the next.
Place both functions inside an object (because globals are bad), add a variable to the object which knows if a function is executing, and check the variable, like this:
var ns = {
isExecuting:false,
func1: function(){
if (this.isExecuting) { return; }
this.isExecuting = true;
//do stuff 1
this.isExecuting = false;
},
func2: function(){
if (this.isExecuting) { return; }
this.isExecuting = true;
//do stuff 2
this.isExecuting = false;
}
}
and for extra elegance:
var ns = {
isExecuting:false,
executeConditionally:function(action){
if (this.isExecuting) { return; }
this.isExecuting = true;
action();
this.isExecuting = false;
}
func1: function(){
this.executeConditionally(function(){
//stuff
})
},
func2: function(){
this.executeConditionally(function(){
//stuff
})
}
}
add a variable globally or in scope outside the method called IsProcessing and set it to true the first time the method is called, on the method you can then just check if (IsProcessing) return false;
All you need to do is set a variable that indicates whether the function is running:
var isRunning = false;
$('a').click(function({
if(isRunning == false){
isRunning = true;
///Do stuff
isRunning = false;
}
else{
return false;
}
});
Don't you want all the clicks to be taken into account, but in order ?
If so, I suggest you separate streams between adding a click to process, and consuming clicks
:
the html :
<a class="example" href="javascript:void(0)" onclick="delayPushLine();">i like pie very much</a><br/>
<a class="example" href="javascript:void(0)" onclick="delayPushLine();">so does the next guy</a>
<div class="writeThere"></div>
and the javascript (using jQuery a bit)
var charDisplayDelay = 50; // the time between each char display
var displayDelay = 2000; // the time before a click is handled
var lines = [];
var lineIdx = 0, pos = 0;
function delayPushLine(e) {
if (!e) {
e = window.event
}
setTimeout(function() { lines.push(e.srcElement.innerText); }, displayDelay);
}
function showLines () {
if (lines.length > lineIdx) {
if (pos < lines[lineIdx].length) {
$(".writeThere").append(lines[lineIdx].substr(pos,1));
pos++;
} else {
pos = 0;
lineIdx++;
$(".writeThere").append("<br/>");
}
}
}
setInterval("showLines()", charDisplayDelay);
http://jsfiddle.net/kD4JL/1/
I have the following code
startProgressTimer: function () {
var me = this,
updateProgressBars = function (eventItems) {
alert("updateProgressBars: looping");
alert("me.eventProgressTimerId:" + me.eventProgressTimerId);
var i = 0;
if (eventItems.length === 0) {
alert("internal Stop Begin")
clearInterval(me.eventProgressTimerId);
alert("internal Stop End")
eventItems = [];
}
for (i = 0; i < eventItems.length; i++) {
if (eventItems[i]._eventId) {
eventItems[i].updateProgressBar();
}
}
};
alert("Start Progress Timer");
this.eventProgressTimerId = setInterval(function () {
updateProgressBars([]);
}, 10000);
}
When the function is called I would expect it to run and bottom out only it keeps on looping.
screen output
ALERT:updateProgressBars: looping
ALERT:me.eventProgressTimerId:10
ALERT:internal Stop Begin
ALERT:internal Stop End
ALERT:updateProgressBars: looping
ALERT:me.eventProgressTimerId:10
ALERT:internal Stop Begin
ALERT:internal Stop End
Any ideas
I suspect the problem might be that the code you don't show calls the startProgressTimer() method more than once for the same instance of whatever object it belongs to, and then within the method you store the interval id in an instance property this.eventProgressTimerId - so multiple calls overwrite the property and you'd only be able to cancel the last one.
If that's the case, a simple fix is to declare your eventProgressTimerId as a local variable within startProgressTimer().