JavaScript异步reduce初始化报错的排查与修复方法
问题根因
- 核心报错原因是
async修饰的reduce回调使用逻辑错误:所有async函数的返回值都是Promise实例,原代码给reduce设置的初始值是Promise.resolve({ files: [], images: [] }),每一轮迭代拿到的累加器acum本质都是上一轮回调返回的Promise对象,不是Promise resolve后得到的实际存储对象。此时直接访问acum.images、acum.files得到的是undefined,调用push方法就会触发类型错误,和运行时报错表现完全一致。 - 原代码还有一处逻辑缺陷:把
setIsLoading的状态切换放在循环内部,多文件上传场景下会反复触发加载状态的开关,不符合正常交互逻辑,也没有处理上传失败时loading状态无法关闭的边界情况。
修复方案
优先选用逻辑更清晰、异步流程更可控的for...of循环替代reduce处理串行上传逻辑,从根源上规避异步累加器的使用坑,同时修正loading状态的触发时机,增加异常兜底:
const handleFileChange = async (e) => { const target = e?.target?.files; if (!target?.length) return; setIsLoading(true); const attachments = { files: [], images: [] }; try { for (const file of Array.from(target)) { file.id = uniqid(); if (file.type.startsWith('image/')) { const response = await channel.sendImage(file); file.src = response.file; attachments.images.push(file); } else { const response = await channel.sendFile(file); file.src = response.file; attachments.files.push(file); } } setFilesList(prev => [...prev, ...attachments.files]); setImagesList(prev => [...prev, ...attachments.images]); } finally { // 无论上传成功/失败,最终都关闭加载状态 setIsLoading(false); } };
如果一定要保留reduce写法,必须在每轮迭代开头先
await累加器Promise,拿到实际存储对象后再做操作,示例如下(不推荐,可读性较差):const attachments = await Array.from(target).reduce(async (acumPromise, file) => { // 等待上一轮累加器的Promise解析为实际对象 const acum = await acumPromise; file.id = uniqid(); if (file.type.startsWith('image/')) { const response = await channel.sendImage(file); file.src = response.file; acum.images.push(file); } else { const response = await channel.sendFile(file); file.src = response.file; acum.files.push(file); } return acum; }, Promise.resolve({ files: [], images: [] }));
内容的提问来源于stack exchange,提问作者william007
相关产品推荐
相关产品推荐

