无法遍历存储自定义类实例的Map对象问题排查
问题结论
遍历存储自定义类实例的Map不需要任何特殊处理,你遇到的问题和自定义类完全无关,是两个JavaScript常见机制叠加导致的误判:
- Node/浏览器控制台对引用类型值的懒打印特性
- 异步流读取的时序错误
为什么会出现Map(0)但展开有6条数据的矛盾
你执行console.log(mgroups)的时刻,mgroups确实是空的,所以控制台第一行会显示Map(0)。
控制台打印对象、Map这类引用类型时,不会在打印瞬间深拷贝快照内容:当你手动点击展开控制台日志条目时,控制台才会去读取该引用指向的内存当前的实际值。这时候异步文件读取已经完成,6条数据都已经写入Map,所以你展开后能看到完整的条目和实例详情。
这也解释了为什么遍历没有任何输出:你写的for...of循环执行时,Map真的是空的,循环体一次都不会进入,自然不会打印任何内容。
异步时序问题的本质
你使用的fs.createReadStream和readline接口都是非阻塞异步API,注册的line、close事件回调不会阻塞后续代码执行。你当前代码的实际执行顺序是:
- 调用
readObjFile,创建文件读流、注册各类事件回调后,函数直接返回 - 紧接着执行你写的Map遍历逻辑,此时文件还在逐行读取过程中,
mgroups内没有任何数据,遍历无输出 - 文件读取完成,触发
close事件回调,所有MaterialGroup实例才被逐一写入mgroups,但此时遍历逻辑早就执行完毕了
修复方案
所有依赖mgroups数据的逻辑,必须等文件读取完成、Map填充完毕后再执行,推荐改造成Promise写法统一异步流程:
const fs = require('fs'); const readline = require('readline'); function readObjFile(objFilePath){ // 初始化要返回的Map const mgroups = new Map(); return new Promise((resolve, reject) => { const objInput = fs.createReadStream(objFilePath); const rl = readline.createInterface({ input: objInput, terminal: false }); let mgroup = new MaterialGroup("NotSet"); // 原有逐行处理逻辑保持不变 rl.on('line', function(line){ const pieces = line.split(" "); // ... 保留你原有的g/usemtl/f/eof判断和写入mgroups的逻辑 }); rl.on('close', () => { // 所有行处理完成后,把填充好的Map返回 resolve(mgroups); }); rl.on('error', err => reject(err)); }) } // 调用时等异步读取完成再遍历 async function run() { const mgroups = await readObjFile('你的目标obj文件路径'); // 此时Map已填充完毕,遍历可正常输出 for(let [key, value] of mgroups){ console.log('key: ' + key) console.log('value: ' + value) console.log('value.name: ' + value.name) } } run();
你可以在遍历前加一句console.log(mgroups.size)快速验证,如果输出为0就完全坐实时序问题,和Map中存储的值类型没有任何关联。
内容的提问来源于stack exchange,提问作者user3064141
相关产品推荐
相关产品推荐

