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 pleted 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 === "plete") {
console.log(`Download ${delta.id} has pleted.`);
currentDownload = false;
}
//}
}
chrome.downloads.onChanged.addListener(handleChanged)
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 pleted 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 === "plete") {
console.log(`Download ${delta.id} has pleted.`);
currentDownload = false;
}
//}
}
chrome.downloads.onChanged.addListener(handleChanged)
Share
Improve this question
asked Jul 30, 2018 at 19:15
EfraínEfraín
5051 gold badge8 silver badges14 bronze badges
7
- You need to use callbacks or use Mozilla WebExtension polyfill and go with Promises or async/await. In short: 1. start a download and specify a callback that will give you an item id, 2. invoke chrome.downloads.search with that id every 1 second e.g. via setInterval 3. when the returned item's state field is plete, increase the current index variable and go to step 1 – woxxom Commented Jul 30, 2018 at 19:24
- Hi @wOxxOm i'm not sure if i wasn't clear, so i'll say it again, i actually did that, i tried and got something like this var downloading = chrome.downloads.download(..., function(id) {chrome.downloads.search({id:id}, function(dwnl) {console.log(dwnl);currentDownload = false;})}); But since the callback seems to be asynchronous, it starts the next download before the callback detect the pleted download. That's where my problem es. – Efraín Commented Jul 30, 2018 at 19:30
- Of course, callbacks are asynchronous and chrome.downloads.download doesn't have any returned value so you need to invoke the code from inside the callback. That's a standard asynchronous pattern. – woxxom Commented Jul 30, 2018 at 19:33
- What do you mean with "Invoke the code from inside the callback"? Which code? The for keeps going independently of what happens inside the callback, i'm right? – Efraín Commented Jul 30, 2018 at 19:36
-
Assuming you go with callbacks no
for
is needed. You could usefor
with await/async. Anyway, this is a standard asynchronous loop pattern so you may want to read an article on that because it's something very useful in extensions where all callbacks are asynchronous. – woxxom Commented Jul 30, 2018 at 19:42
1 Answer
Reset to default 10Callbacks:
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 === 'plete');
}
});
});
}
Usage: await downloadSequentially(arrayOfStringUrls)
- inside an async
function.