用Map.entries()分批次持久化的核心是利用其惰性迭代特性,按固定大小拆分处理键值对,避免内存溢出;支持同步计数、异步并发(如p-limit限流)及可暂停的进阶封装。

用 Map 的 entries() 迭代器做分批次持久化,核心是把大 Map 拆成小块,每次只处理固定数量的键值对,避免内存溢出或数据库压力过大。
理解 entries() 返回的是可迭代对象,不是数组
map.entries() 返回一个迭代器(Iterator),它按插入顺序逐个产出 [key, value] 数组。它不一次性加载全部数据到内存,适合流式处理:
const map = new Map([['a', 1], ['b', 2], ['c', 3], ['d', 4]]);
const iter = map.entries();
console.log(iter.next()); // { value: ['a', 1], done: false }
console.log(iter.next()); // { value: ['b', 2], done: false }
这意味着你可以手动控制“拉取”多少次,实现自然的分批。
手动控制批次:用 for...of + 计数器
最直接的方式是遍历迭代器,每累积够 N 项就执行一次持久化操作:
- 创建迭代器(调用
map.entries()) - 用
for...of遍历,用变量计数 - 每满 batchsize 就调用一次写入函数(如批量插入数据库)
- 别忘了处理剩余不足一批的数据
function persistInBatches(map, batchSize = 100, writeFn) {
const iterator = map.entries();
let batch = [];
for (const entry of iterator) {
batch.push(entry);
if (batch.length === batchSize) {
writeFn(batch); // 例如:await db.bulkInsert(batch)
batch = [];
}
}
if (batch.length > 0) {
writeFn(batch);
}
}
配合异步写入:避免阻塞,但需注意顺序与错误处理
如果 writeFn 是异步的(如调用 API 或数据库),不能直接在 for...of 中 await——那会变成串行,失去并发优势。更合理的做法是:
- 先收集批次,再并发提交(适合幂等写入)
- 或使用
for await...of+ 限流(如 p-limit 库)来控制并发数 - 捕获单个批次错误,记录日志并继续,避免整批失败中断流程
示例(简单并发 3 批):
import pLimit from 'p-limit';
const limit = pLimit(3);
async function persistAsyncBatches(map, batchSize = 100, writeFn) {
const iterator = map.entries();
const batches = [];
let batch = [];
for (const entry of iterator) {
batch.push(entry);
if (batch.length === batchSize) {
batches.push(batch);
batch = [];
}
}
if (batch.length) batches.push(batch);
const results = await Promise.allSettled(
batches.map(b => limit(() => writeFn(b)))
);
results.forEach((r, i) => {
if (r.status === 'rejected') {
console.error(`批次 ${i} 写入失败:`, r.reason);
}
});
}
进阶:封装成可暂停/恢复的迭代器(适合超大 Map 或断点续传)
若 Map 数据来自流式生成、或需要支持暂停(如用户取消、系统负载高),可以包装迭代器,暴露 nextBatch() 方法:
- 内部维护迭代器状态和当前 batch 缓存
- 每次调用返回 Promise
- 便于与 UI 进度条、重试逻辑、checkpoint 存储结合
这种模式在 ETL 工具或后台任务中更实用,但日常场景中前两种已足够。

















