You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript浏览器多文件下载需设置超时的问题咨询

Hey there! I’ve run into this exact multi-file download quirk with downloadjs before—let’s break down why it’s happening and how to fix it properly.

The issue boils down to how browsers handle rapid download triggers and how downloadjs works under the hood. Each call to downloadjs creates a temporary <a> element, sets its attributes, and triggers a click to start the download. If you call it back-to-back instantly, the browser’s event loop doesn’t get a chance to fully process the first download request before the second one overwrites the temporary element or cancels the pending operation. That’s why only the last file ends up downloading.

Solution 1: Serialize Downloads with Promises (No Hardcoded Delays)

Instead of firing all downloads at once, we can wrap each download in a Promise and execute them one after another using async/await. This ensures the browser finishes processing one download before starting the next, no arbitrary delay needed.

First, wrap downloadjs in a Promise-friendly function:

const downloadAsync = (data, filename, mime) => {
  return new Promise((resolve) => {
    downloadjs(data, filename, mime);
    // Give the browser a tiny window to process the download trigger
    setTimeout(resolve, 50);
  });
};

Then create a batch download function that processes files sequentially:

async function batchDownload(files) {
  for (const file of files) {
    await downloadAsync(file.data, file.filename, file.mime);
  }
}

// Example usage
const myFiles = [
  { data: "First file content", filename: "file1.txt", mime: "text/plain" },
  { data: "Second file content", filename: "file2.txt", mime: "text/plain" },
  { data: "Third file content", filename: "file3.txt", mime: "text/plain" }
];

batchDownload(myFiles);

Solution 2: Precise Download Completion Listening

For even more reliability (no reliance on setTimeout), we can create the download link manually and listen for events that confirm the download has started or completed. This avoids guessing how long to wait:

const downloadAsync = (data, filename, mime) => {
  return new Promise((resolve, reject) => {
    // Create a blob from your in-memory data
    const blob = new Blob([data], { type: mime });
    const url = URL.createObjectURL(blob);
    
    const downloadLink = document.createElement("a");
    downloadLink.href = url;
    downloadLink.download = filename;
    
    // Clean up and resolve when the download processes
    downloadLink.addEventListener("load", () => {
      URL.revokeObjectURL(url);
      resolve();
    });
    
    // Handle errors
    downloadLink.addEventListener("error", (error) => {
      URL.revokeObjectURL(url);
      reject(error);
    });
    
    // Trigger the download
    downloadLink.click();
    
    // Clean up the element after a short delay
    setTimeout(() => document.body.removeChild(downloadLink), 100);
  });
};

// Use the same batchDownload function from Solution 1
async function batchDownload(files) {
  for (const file of files) {
    await downloadAsync(file.data, file.filename, file.mime);
  }
}

Why the 100ms Delay Worked

Your original fix with a 100ms gap worked because it gave the browser enough time to fully process each download trigger—creating the link, firing the click event, and queuing the download—before the next call came in. The problem with hardcoded delays is they’re one-size-fits-all; a slow browser or large file might need longer, while a fast setup could waste time. The Promise-based approach adapts better.

Also, keep in mind that browsers have built-in protections against spammy downloads, so serializing requests helps avoid getting blocked entirely.

内容的提问来源于stack exchange,提问作者funseiki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:37:57