2

I have a web application, in which I have two buttons: "start download" and "abort download", that are bind to the methods: start() and stop() accordingly.
1. When the user clicks the "start download" button, the application will loop over an array of data. For each item in the array, it will call the _downloadFile() method that will download one file and return a new Promise(), once that download is complete the next download will start.
2. If during the download flow (that may take a long long time) the user clicked the "abort download" button I would like to stop the download flow.

How can I implement the abort functionality?
Here is what I got so far?

Notice that due to the fact I'm using async/await, the loop will holt until the promise is resolved, and the next iteration will only be executed once the promise is resolved (or rejected).

async function start(dataArray)
{
  for (let i = 0; i < dataArray.length; i++)
  {
    try
    {
      let fileUrl = `http://server-url/${ i }/${ dataArray[i] }.xml`;
      let result = await _downloadFile(fileUrl);
      _saveResult(result);
    }
    catch(e) // promise rejected
    {
      _handleError(e);
    }
  }
}

function stop()
{
  // what goes here?? how do I stop the flow??
}

function _downloadFile(fileUrl)
{
  return new Promise((resolve, reject) =>
  {
    // ...
  });
}

function _saveFile(data) 
{
  // ..
}

function _handleError(error)
{
  console.error("Error: " + error);
}
Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
  • What if some of the items have already been downloaded prior to `stop` being called? – T.J. Crowder Mar 12 '18 at 10:11
  • Your code does the downloads sequentially rather than in parallel; the browser can probably handle at least two if not four in parallel, which may be faster for the user. – T.J. Crowder Mar 12 '18 at 10:14
  • It doesn't matter. Basically, I want to keep the data that where already saved, but this doesn't really matter because if I don't want that data I can easily delete it in the **stop** flow – Gil Epshtain Mar 12 '18 at 10:16
  • @T.J. Crowder, yes I know that the download is sequential - this is by design. Since the files count is very very large. Trying to download the files parallel will cause a few problems: 1 - the server can't handle all that load and will start rejecting files at some point. 2 - The browser may end on resources (an actual error I got a few times from the browser). 3 - In some cases, I need the previously downloaded info for the future download (order matter). Nevertheless, the fact that the download is sequentially and not parallel doesn't relevant to the quastion. – Gil Epshtain Mar 12 '18 at 10:23
  • No, it's not, I just thought I'd point it out in case you didn't realize (a lot of people don't understand this stuff as well as you apparently do). Sadly, JavaScript's native promises (and `await`) don't have built-in support for parallel-with-concurrency-limit like some advanced promise libs do. – T.J. Crowder Mar 12 '18 at 10:31

1 Answers1

2

Set a flag that the function can check, and throw if it's set. (Well, you're actually converting rejection to resolution in your code, so maybe you don't throw, just return early. [I wouldn't do that, btw.])

let stopped = false; // *** The flag
async function start(dataArray)
{
  stopped = false;                                  // *** Re-init the flag
  for (let i = 0; i < dataArray.length; i++)
  {
    try
    {
      let fileUrl = `http://server-url/${ i }/${ dataArray[i] }.xml`;
      let result = await _downloadFile(fileUrl);
      if (stopped) {                                // *** Check the flag
          return;                                   // *** and return
      }
      _saveResult(result);
    }
    catch(e) // promise rejected
    {
      _handleError(e);
    }
  }
}

function stop()
{
  // *** Set the flag
  stopped = true;
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875