0

I've made some changes in the previous code, running more tests, and I've run into the same problem again (Is it possible continue a stopped async function?). So this question is related.

Furthermore, the previous solution does not work.

var stop = false;
var pause = false;
var elParrafo = document.getElementById('miParrafo');

function sendPause() {
  pause = true;
}

function sendContinue() {
  pause = false;
}

function sendStop() {
  stop = true;
}

function longWork(ms, char) {
  return new Promise(function(resolve, reject) {
    elParrafo.innerHTML += char;
    setTimeout(resolve, ms);
  });
}

async function doIt() {
  for (var i = 0; i < 666; i++) {
    if (!stop) {
      if (!pause) {
        await longWork(50," > ");
      }
    } else {
      break;
    }
  }
}

doIt();
<form class="" action="index.html" method="post">
  <input type="button" value="Pause" onclick="sendPause()">
  <input type="button" value="Continue" onclick="sendContinue()">
  <input type="button" value="Stop" onclick="sendStop()">
</form>
<p id="miParrafo"></p>
  • I'm not sure if you understand promises or `async await`. The code you posted could be done much simpler with `setTimeout` or `setInterval`. If you have long running code that blocks your UI then you should use web workers. Info on why and how to promise: https://stackoverflow.com/a/47678417/1641941 – HMR Dec 07 '17 at 14:03

3 Answers3

4

When you pause the task, you must actually pause it and wait until the user wants to continue, not continue your loop till the end but without doing any work.

var stopped;
var paused;
var waitingContinuations;

function sendStart() {
  stopped = false;
  paused = false;
  waitingContinuations = [];
  doIt();
}

function sendPause() {
  paused = true;
}

function sendContinue() {
  paused = false;
  for (const cont of waitingContinuations)
    cont();
  waitingContinuations.length = 0;
}

function sendStop() {
  stopped = true;
}

async function doIt() {
  document.getElementById('output').textContent = "";
  for (var i = 0; i < 666; i++) {
    // in places where you might want to abort, put this:
    if (stopped) return; // or throw new Error("stopped");
    // in places where you might want to pause, put this:
    if (paused) await new Promise(resolve => waitingContinuations.push(resolve));

    await longWork(50, " > ");
  }
}

function longWork(ms, char) {
  return new Promise(resolve => {
    document.getElementById('output').textContent += char;
    setTimeout(resolve, ms);
  });
}
<form>
  <input type="button" value="Start" onclick="sendStart()">
  <input type="button" value="Pause" onclick="sendPause()">
  <input type="button" value="Continue" onclick="sendContinue()">
  <input type="button" value="Stop" onclick="sendStop()">
</form>
<p id="output"></p>
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • `new Promise(resolve => waitingContinuations.push(resolve));` How is equivalent this line without the arrow syntax? – robe007 Dec 10 '17 at 20:26
  • 2
    @robe007 http://babeljs.io/repl/#?code_lz=HYUw7gBACgTg9gWwJYGcQAoYhXANgNxAgF4A-CMAQyQBclgBzAYTmDuAFdK7WUA6AA4cUAC0zY8hAJRSA3EA – Bergi Dec 10 '17 at 21:24
  • So, it is possible to `return` inside a `Promise` without a `resolve` or `reject`? Why? – robe007 Dec 10 '17 at 21:28
  • A `return` that happens asynchronously is useless - so I think your answer is *no*. – Bergi Dec 10 '17 at 21:33
  • So `new Promise(function (resolve) { return waitingContinuations.push(resolve); });` it uses `return` inside the `Promise`. How you explain that? – robe007 Dec 10 '17 at 21:36
  • The promise constructor ignores that return value. It's just that `x => arr.push(x)` is shorter to write than `x => { arr.push(x); }` (which doesn't return anything) – Bergi Dec 10 '17 at 21:49
  • So, when the `Promise` is _resolved_ if ignore the `return`?. And .... according to the logic of the code: what means to _push_ a `resolve` inside the array `waitingContinuations`? Thanks for the patience – robe007 Dec 10 '17 at 22:09
  • The promise is getting resolved when `resolve` is called. Which happens in `sendContinue`, where it calls all the functions stored in the `waitingContinuatons` array. – Bergi Dec 10 '17 at 22:12
3

The issue occurs when you set the flags to true. The async code exhausts the rest of the loop doing nothing by the time you unset the flags. You can get around it with some promises instead.

let stop = false;
let isPaused = false;
let resolvePause = () => {};
let pauseProm = Promise.resolve();
const elParrafo = document.getElementById('miParrafo');

function sendPause() {
  if(isPaused) return;

  isPaused = true;
  pauseProm = new Promise(resolve => resolvePause = resolve);
}

function sendContinue() {
  isPaused = false;
  resolvePause();
}

function sendStop() {
  stop = true;
}

function longWork(ms, char) {
  return new Promise(function(resolve, reject) {
    elParrafo.innerHTML += char;
    setTimeout(resolve, ms);
  });
}

async function doIt() {
  for (let i = 0; i < 666; i++) {
    if (stop) break;

    await pauseProm;
    await longWork(50," > ");
  }
}

doIt();
<form class="" action="index.html" method="post">
  <input type="button" value="Pause" onclick="sendPause()">
  <input type="button" value="Continue" onclick="sendContinue()">
  <input type="button" value="Stop" onclick="sendStop()">
</form>
<p id="miParrafo"></p>
kamoroso94
  • 1,713
  • 1
  • 16
  • 19
  • @kamoroso94 What does exactly does this line: `new Promise(resolve => resolvePause = resolve);` It is possible to return inside a promise without a `resolve` or `reject`? – robe007 Dec 09 '17 at 23:15
  • @kamoroso94 Can you explain a little bit more about how `sendPause()` and `sendContinue()` works, please? – robe007 Dec 09 '17 at 23:21
0

Here is a simple solution that uses setInterval:

var task = (function(){
  var timer;
  var taskImplementation;
  var max;
  var ms;
  var timesRun = 0;
  var isPaused = false;
  const runTask = function(){
    if(!isPaused && timesRun<max){
      taskImplementation();
      timesRun++;
    }else{
      clearInterval(timer);
    }
  };
  const reset = function() {
    timesRun=0;
    isPaused=false;    
  };
  const start = function(){
    if(timer){
      stop();
    }
    reset();
    timer = setInterval(runTask,ms);
  };
  const stop = function(){
    reset();
    clearInterval(timer);
  };
  const pause = function(){
    clearTimeout(timer);
  };
  const cont = function(){
    timer = setInterval(runTask,ms);    
  };
  return function(doWhat){
    taskImplementation = doWhat;
    return function(interval){
      ms = interval;
      return function(howManyTimes){
        max = howManyTimes;        
        return {
          start:start,
          stop:stop,
          pause:pause,
          continue:cont
        }
      }
    }
  }
})();
var doWhat = (function(elParrafo){
  return function(){
    elParrafo.innerHTML += " > ";
  }
})(document.getElementById('miParrafo'));
var interval =200;//how long to wait between task execution
var howManyTimes = 666;//how many times to repeat task
window.setParrafoTask = task(doWhat)(interval)(howManyTimes);
    <form class="" action="index.html" method="post">
        <input type="button" value="Start" onclick="setParrafoTask.start()">
        <input type="button" value="Pause" onclick="setParrafoTask.pause()">
        <input type="button" value="Continue" onclick="setParrafoTask.continue()">
        <input type="button" value="Stop" onclick="setParrafoTask.stop()">
      </form>
      <p id="miParrafo"></p>
HMR
  • 37,593
  • 24
  • 91
  • 160