await未完成时then提前执行:表单文件转Base64后AJAX请求异常
问题分析与解决方案
核心问题
loopChildren异步等待失效:$(children).each的回调是异步函数,但each方法本身不会等待这些异步操作完成,导致resolve(allData)在文件等异步内容处理完成前就执行,传给AJAX的allData不完整。getContent多文件逻辑错误:遍历文件时第一个文件处理完就直接resolve,后续文件未处理,也无法返回多文件的Base64数组。- 冗余Promise嵌套:部分函数混用
async/await和.then,逻辑混乱易出错。
修改后的完整代码
$(document).ready(function () { $("form").submit(async function (e) { e.preventDefault(); const children = $(this).find("input, textarea"); console.log(children.length); const nodeId = $(this).attr("data-nodeId"); // 简化流程:先获取完整数据再调用AJAX const allData = await loopChildren(children); ajaxCall(allData, nodeId); }) }); // 重构loopChildren:用Promise.all等待所有异步操作完成 function loopChildren(children) { // 遍历表单元素,生成每个元素的处理Promise const promises = $(children).map(async function(index, elem) { const $elem = $(elem); const dataType = getDataType($elem.attr("type")); const content = await getContent(elem); console.log(`content ${index} :`, content); return { nodeId: parseInt($elem.attr("id")), content: content, dataType: dataType }; }).get(); // 把jQuery对象转为普通数组 // 等待所有Promise完成后返回结果数组 return Promise.all(promises); } // 重构getContent:修复多文件处理逻辑,简化Promise返回 function getContent(elem) { const $elem = $(elem); const type = $elem.attr("type"); if (type === "checkbox" || type === "radio") { return Promise.resolve($elem.is(":checked")); } else if (type === "file") { const files = Array.from($elem[0].files); if (files.length === 0) { return Promise.resolve(null); // 无文件时返回null } else if (files.length === 1) { return processFile(files[0]); // 单文件返回单个Base64 } else { // 多文件返回Base64数组 return Promise.all(files.map(file => processFile(file))); } } else { return Promise.resolve($elem.val()); } } // 原readFileAsync逻辑保留 function readFileAsync(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(file); }) } // 简化processFile:去掉冗余Promise嵌套 async function processFile(file) { try { const content = await readFileAsync(file); console.log(content); return content; } catch (err) { console.log(err); throw err; } } function ajaxCall(myVeryOwnDataObject, nodeID) { console.log(JSON.stringify(myVeryOwnDataObject)); $.ajax({ type: "POST", url: "/Umbraco/Api/Form/SendForm", contentType: "application/json; charset=utf-8", headers: { 'nodeId': nodeID }, data: JSON.stringify(myVeryOwnDataObject), success: function (success) { console.log(success); }, error: function (error) { console.log(error); } }) } function getDataType(element) { switch (element) { case "text": case "email": case "tel": case "url": case "color": return "text"; case "textarea": return "textarea"; case "number": case "range": return "number"; case "date": case "month": return "date"; case "datetime-local": return "dateTime"; case "time": return "time"; case "file": return "file"; case "checkbox": case "radio": return "check"; default: return "text"; // 增加默认值,避免未匹配类型返回undefined } }
关键改动说明
loopChildren:将each改为map生成Promise数组,用Promise.all等待所有异步操作完成,确保allData收集完所有表单数据后再传给AJAX。getContent:修复多文件处理逻辑,支持返回单文件Base64或多文件Base64数组,简化Promise返回方式。processFile:去掉冗余的Promise嵌套,用async/await简化异步流程,代码更清晰。getDataType:合并相同返回值的case,增加默认值,避免未匹配类型返回undefined。
内容的提问来源于stack exchange,提问作者anvbis
相关产品推荐
相关产品推荐

