最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to make Chrome Downloads API wait until a download has ended? - Stack Overflow

programmeradmin1浏览0评论

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 use for 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
 |  Show 2 more ments

1 Answer 1

Reset to default 10

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 === 'plete');
      }
    });
  });
}

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

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>