Efraín
Efraín

Reputation: 505

How to make Chrome Downloads API wait until a download has ended?

I'm having a problem while trying to make an extension. What the extension does is pretty simple, I have a URL with a ser of images, i get the src of all the images, and then use the Downloads API of chrome to download them on a folder. I have it, and works great, but there is a problem, all the downloads start consecutively, which constantly makes some downloads to fail, so i tried to make Chrome wait until a download has completed to start another.

First i tried to search if the Downloads API has a way to verify this, but, at least from what i've searched, i haven't find a method to get the DownloadItem, there is only two ways to do this, with search and the own download method, but both use a callback that seems to be asynchronous, then i tried to add a while before the download, and change it's condition with the one of those methods, but alway loops itself because while it's in the while loop, doesn't continue the callback of them, same with global methods like handleChanged(). So, how could i make Chrome wait until a download has ended before starting another without looping itself?

This is the part of the code i use for the downloads

    for(let i=0; i<images.length; i++) {
        // Download image
        while(currentDownload) {
            if (cont == 10000000) {
                currentDownload = false;
            } else {
                cont = cont + 1;
            }
        };
        cont = 0;
        currentDownload = true;
        var downloadUrl = images[i].split(" . ")[0];
        img = images[i].split(" . ")[1];
        console.log(name+"/"+img);
        var downloading = chrome.downloads.download({
            url: downloadUrl,
            filename: name+"/"+img,
            conflictAction: 'uniquify'
        });

    }

I put a counter on the while because the looping of my other tests were making my browser to crash, but it's better if there is a way to check until the download has ended before starting the next one. This is the listener i'm using to make the changes, just to be clear, i tried to put the search method on the prev code, even inside the while, and it just didn't worked. currentDownload is a global var.

function handleChanged(delta) {
    //if (urlRegexD.test(pest)) {

        if (delta.state && delta.state.current === "complete") {
            console.log(`Download ${delta.id} has completed.`);
            currentDownload = false;

        }
    //}
}

chrome.downloads.onChanged.addListener(handleChanged)

Upvotes: 4

Views: 5510

Answers (1)

woxxom
woxxom

Reputation: 73526

Callbacks:

Extract one "step" into a function and invoke it from onChanged event listener.

function downloadSequentially(urls, callback) {
  let index = 0;
  let currentId;

  chrome.downloads.onChanged.addListener(onChanged);

  next();

  function next() {
    if (index >= urls.length) {
      chrome.downloads.onChanged.removeListener(onChanged);
      callback();
      return;
    }
    const url = urls[index];
    index++;
    if (url) {
      chrome.downloads.download({
        url,
      }, id => {
        currentId = id;
      });
    }
  }

  function onChanged({id, state}) {
    if (id === currentId && state && state.current !== 'in_progress') {
      next();
    }
  }
}

Usage: downloadSequentially(arrayOfStringUrls, () => console.log('done'))


async/await:

Wrap the API calls in Promise and await them.

async function downloadSequentially(urls) {
  for (const url of urls) {
    if (!url) continue;
    const currentId = await download(url);
    const success = await onDownloadComplete(currentId);
  }
}

function download(url) {
  return new Promise(resolve => chrome.downloads.download({url}, resolve));
}

function onDownloadComplete(itemId) {
  return new Promise(resolve => {
    chrome.downloads.onChanged.addListener(function onChanged({id, state}) {
      if (id === itemId && state && state.current !== 'in_progress') {
        chrome.downloads.onChanged.removeListener(onChanged);
        resolve(state.current === 'complete');
      }
    });
  });
}

Usage: await downloadSequentially(arrayOfStringUrls) - inside an async function.

Upvotes: 10

Related Questions