JavaScript:如何在Promise内forEach循环最后实例输出全局错误提示
问题
开发文件上传组件时,前端已实现图片分辨率验证逻辑:通过URL.createObjectURL()生成预览图,用decode()获取原图分辨率,不符合2MP要求的图片添加单独错误提示。但全局错误提示因放在forEach循环内,多文件部分出错时会多次渲染导致闪烁,需求是仅在所有文件验证完成后显示一次全局提示。
解决方案
方法1:利用forEach索引判断最后一项(需处理异步)
修改forEach调用,传入索引和原数组,在每个文件的异步验证完成后,判断是否是最后一个文件,再统一检查全局错误:
submitData = new DataTransfer(); attachFiles.addEventListener("change", (e) => { // 原有处理逻辑... // 调用showFiles时传入索引和数组 [...submitData.files].forEach((file, index, array) => { showFiles(file, index, array); }); }); function showFiles(file, index, array) { let previewImage = new Image(); previewImage.className = "upload-preview-image"; previewImage.src = URL.createObjectURL(file); previewImage.decode().then((response) => { let w = previewImage.naturalWidth; let h = previewImage.naturalHeight; let resError = `<p class="upload-preview-error">Image must be bigger than 2MP</p>` if(w * h < 2000000) { previewImage.insertAdjacentHTML('beforebegin', resError); } // 仅最后一个文件验证完成后处理全局提示 if (index === array.length - 1) { updateGlobalError(); } }).catch((encodingError) => { console.error('图片解码失败:', encodingError); // 解码失败时,若为最后一个文件仍需处理全局提示 if (index === array.length - 1) { updateGlobalError(); } }); } // 抽离全局提示更新逻辑 function updateGlobalError() { let imgError = document.querySelectorAll('.upload-preview-error'); const errorContainer = document.querySelector('.js-upload-error'); if(imgError.length) { errorContainer.innerHTML = `<p class="warning">YOU HAVE ERRORS. CHECK YOUR IMAGES BELOW</p>` } else { errorContainer.innerHTML = ''; } }
方法2:用Promise.all统一处理异步验证(更可靠)
由于decode()是异步操作,文件验证完成顺序可能和循环顺序不一致,用Promise.all等待所有验证完成后再处理全局提示,确保结果准确:
submitData = new DataTransfer(); attachFiles.addEventListener("change", (e) => { // 原有处理逻辑... const files = [...submitData.files]; // 收集所有文件的验证Promise const validationTasks = files.map(file => { return new Promise(resolve => { let previewImage = new Image(); previewImage.className = "upload-preview-image"; previewImage.src = URL.createObjectURL(file); previewImage.decode().then(() => { const w = previewImage.naturalWidth; const h = previewImage.naturalHeight; const resError = `<p class="upload-preview-error">Image must be bigger than 2MP</p>` if(w * h < 2000000) { previewImage.insertAdjacentHTML('beforebegin', resError); } // 将预览图添加到DOM(根据实际需求补充) // document.querySelector('.upload-preview-container').appendChild(previewImage); resolve(); }).catch(err => { console.error('图片解码失败:', err); // 解码失败仍标记任务完成,避免Promise.all阻塞 resolve(); }); }); }); // 所有验证完成后更新全局提示 Promise.all(validationTasks).then(() => { const errorContainer = document.querySelector('.js-upload-error'); const hasErrors = document.querySelectorAll('.upload-preview-error').length > 0; errorContainer.innerHTML = hasErrors ? `<p class="warning">YOU HAVE ERRORS. CHECK YOUR IMAGES BELOW</p>` : ''; }); });
方法3:维护错误状态变量(性能更优)
在循环外定义错误状态,验证出错时标记,所有任务完成后直接根据状态更新提示,无需查询DOM:
submitData = new DataTransfer(); attachFiles.addEventListener("change", (e) => { // 原有处理逻辑... const files = [...submitData.files]; let hasValidationError = false; const validationTasks = files.map(file => { return new Promise(resolve => { let previewImage = new Image(); previewImage.className = "upload-preview-image"; previewImage.src = URL.createObjectURL(file); previewImage.decode().then(() => { const w = previewImage.naturalWidth; const h = previewImage.naturalHeight; const resError = `<p class="upload-preview-error">Image must be bigger than 2MP</p>` if(w * h < 2000000) { previewImage.insertAdjacentHTML('beforebegin', resError); hasValidationError = true; } // 添加预览图到DOM // document.querySelector('.upload-preview-container').appendChild(previewImage); resolve(); }).catch(err => { console.error('图片解码失败:', err); hasValidationError = true; resolve(); }); }); }); Promise.all(validationTasks).then(() => { const errorContainer = document.querySelector('.js-upload-error'); errorContainer.innerHTML = hasValidationError ? `<p class="warning">YOU HAVE ERRORS. CHECK YOUR IMAGES BELOW</p>` : ''; }); });
内容的提问来源于stack exchange,提问作者paulo77
相关产品推荐
相关产品推荐

