generator 结合 for await...of 可实现可控且内存友好的流式文件下载——边接收、边处理、边写入,避免全量加载;通过异步生成器封装 readablestream,逐块读取并 yield 解码后的 uint8array,再用 for await...of 消费写入文件或解析为 jsonl 等结构化数据。

Generator 结合 for await...of 可以让流式文件下载既可控又内存友好——它不把整个文件加载进内存,而是边接收、边处理、边写入,适合大文件或弱网环境。
用异步生成器封装可读流(ReadableStream)
浏览器中 response.body 是一个 ReadableStream,天然支持异步迭代。异步生成器可将其逐块读取并 yield 解码后的内容:
- 调用
response.body.getReader()获取 reader - 在
while(true)中循环调用reader.read(),直到done === true - 对每块
value(Uint8Array)做必要处理(如转为 Blob、Base64 或直接写入 FileWriter) - yield 处理后的 chunk,而非原始 Promise,确保消费者拿到的是已解析值
示例:
async function* downloadChunks(url) {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
<p>const reader = res.body.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 可选:转换为 Blob / Text / ArrayBuffer 等
yield value; // Uint8Array,可直接用于 Blob 或流式写入
}
} finally {
reader.releaseLock();
}
}</p>用 for await...of 消费并分块写入
消费时无需手动管理 reader 或 Promise 链,for await...of 自动等待每个 chunk 到达,适合控制写入节奏:
- 在
async函数内使用,避免语法错误 - 每收到一块,就创建临时 Blob 并追加到文件(如用
FileSystemWritableFileStream) - 可嵌入进度更新、中断检查、限速逻辑(例如
await sleep(10)控制吞吐) - 错误统一用
try/catch包裹整个循环,reader 已在生成器内释放,安全可靠
示例:
(async () => {
try {
const fileHandle = await window.showSaveFilePicker({ types: [{ description: 'Binary', accept: { '*/*': ['.bin'] } }] });
const writable = await fileHandle.createWritable();
<pre class="brush:php;toolbar:false;">for await (const chunk of downloadChunks('https://example.com/large.bin')) {
await writable.write(chunk); // 流式写入
console.log(`Wrote ${chunk.length} bytes`);
}
await writable.close();
console.log('Download complete');} catch (err) { console.error('Download failed:', err); } })();
配合 AI 或服务端流式响应的扩展用法
若后端返回的是文本流(如 SSE、JSONL、或 LLM token 流),生成器可进一步解码和结构化:
- 对每块
Uint8Array调用new TextDecoder().decode()得到字符串 - 按换行符
\n分割,逐行 yield 解析后的 JSON 对象(如下载日志、AI 响应片段) - yield 的不再是二进制,而是业务语义明确的对象,下游可直接渲染或聚合
例如处理 JSONL 日志流:
async function* parseJsonlStream(url) {
for await (const chunk of downloadChunks(url)) {
const text = new TextDecoder().decode(chunk);
text.split('\n').forEach(line => {
if (line.trim()) {
try {
yield JSON.parse(line); // 每行一个 JSON 对象
} catch (e) {
console.warn('Invalid JSON line:', line);
}
}
});
}
}关键注意事项
实际部署时需留意几个易错点:
-
for await...of必须在async函数或type="module"脚本中运行,普通<script></script>会报语法错误 - 不要在生成器里
yield fetch()—— 这会 yield Promise;应yield await fetch().then(r => r.arrayBuffer())或直接 yield 解码后的数据 - 大文件下载建议配合
AbortSignal,在生成器内监听中断信号,及时退出循环并释放 reader - Service Worker 环境下,
response.body同样可用,适合离线缓存 + 流式恢复场景
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











