Google Apps Script网页表单输入校验异步响应延迟问题咨询
解决方案
问题根因
- 前端异步逻辑错误:
google.script.run是异步API,发起请求后不会阻塞后续代码执行,原代码将校验、焦点跳转逻辑写在请求发起之后,还没等服务端返回结果就已经执行完成,导致校验完全失效 - 服务端性能瓶颈:每次校验请求都重复读取电子表格整列数据,频繁IO操作导致接口返回耗时长达5秒
一、前端代码修复
核心修改点:
- 新增请求状态锁,避免重复提交
- 将所有校验判断、焦点跳转、表单提交逻辑全部迁移到异步回调
onSuccess中执行 - 增加异常处理逻辑,覆盖请求失败场景
修改后的前端JS代码:
const inputs = document.querySelector('.dws-input'); const formControl = document.querySelectorAll('.form-control'); let curInpID; let firstValid = false, secValid = false, thirdValid = false, allValid = false; // 新增请求锁,避免重复发起请求 let isRequesting = false; formControl[0].focus(); function keyPressFunction(ev) { if (ev.code !== 'Enter' || isRequesting) return; const curInput = ev.target; const inputData = curInput.value; const btnReset = document.getElementById("del"); // 清空之前的校验状态 curInput.classList.remove("is-valid", "is-invalid"); // 加锁,禁用当前输入框避免重复操作 isRequesting = true; curInput.disabled = true; curInpID = curInput.id; // 发起异步校验请求 google.script.run .withSuccessHandler(onSuccess.bind(this, curInput, btnReset)) .withFailureHandler(onError.bind(this, curInput)) .searchData(inputData, curInpID); } function onSuccess(curInput, btnReset, findData) { // 释放请求锁 isRequesting = false; curInput.disabled = false; // 校验失败逻辑 if (!findData) { curInput.classList.add("is-invalid"); curInput.focus(); return; } // 校验成功逻辑 curInput.classList.add("is-valid"); // 更新各字段校验状态 if (curInpID == "tLogin") firstValid = true; if (curInpID == "tTable" && firstValid) secValid = true; if (curInpID == "tOrder" && firstValid && secValid) thirdValid = true; btnReset.disabled = !firstValid; allValid = firstValid && secValid && thirdValid; // 跳转到下一个空输入框 for (const i of formControl) { if (i.value === '') { i.focus(); break; } } // 全部校验通过后提交数据 if (allValid){ const userInfo = { login: document.getElementById("tLogin").value, table: document.getElementById("tTable").value, order: document.getElementById("tOrder").value }; google.script.run.userClicked(userInfo); // 重置后两个输入框 document.getElementById("tTable").value = ''; document.getElementById("tOrder").value = ''; secValid = false; thirdValid = false; document.getElementById("tTable").focus(); } } // 新增请求失败处理逻辑 function onError(curInput) { isRequesting = false; curInput.disabled = false; curInput.classList.add("is-invalid"); curInput.focus(); } inputs.addEventListener('keydown', keyPressFunction);
二、服务端性能优化
核心修改点:
- 校验数据缓存到Apps Script缓存服务,避免每次请求都读取电子表格,大幅降低IO耗时
- 优化数据读取逻辑,减少
getValues调用次数 - 修复原代码
Logger.login拼写错误
修改后的服务端代码:
const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheetTo = ss.getSheetByName("@sistem"); const sheetIn = ss.getSheetByName("@packing"); // 缓存有效时间,单位秒,可根据数据更新频率调整 const CACHE_TTL = 3600; function doGet(e){ const htmlServ = HtmlService.createTemplateFromFile("index"); return htmlServ.evaluate(); } function userClicked(userInfo){ sheetIn.appendRow([userInfo.login, userInfo.table, userInfo.order, new Date()]); Logger.log(userInfo.name + "clicked the button"); } function include(filename){ return HtmlService.createHtmlOutputFromFile(filename).getContent(); } // 新增:从缓存获取校验数据,不存在则从表格读取 function getValidateData() { const cache = CacheService.getScriptCache(); const cached = cache.get("validate_data"); if (cached) return JSON.parse(cached); // 一次读取所有需要的列,避免多次IO const allData = sheetTo.getDataRange().getValues(); const arrLogins = allData.map(row => row[0]).filter(Boolean); const curTMPLORDS = allData.map(row => row[10]).filter(Boolean); const curTABLES = allData.map(row => row[17]).filter(Boolean); const data = {arrLogins, curTMPLORDS, curTABLES}; cache.put("validate_data", JSON.stringify(data), CACHE_TTL); return data; } function searchData(inputData, curInpID){ const {arrLogins, curTMPLORDS, curTABLES} = getValidateData(); let result = false; if (curInpID == "tLogin"){ result = arrLogins.includes(inputData); } else if (curInpID == "tTable"){ result = curTABLES.includes(inputData); } else if (curInpID == "tOrder"){ for (let regexStr of curTMPLORDS) { if (new RegExp(regexStr, "i").test(inputData)) { result = true; break; } } } return result; }
额外优化建议
如果校验数据更新频率较高,可在数据更新的逻辑后增加清空缓存的代码,保证数据一致性:
// 数据更新后调用清空缓存 function clearValidateCache() { CacheService.getScriptCache().remove("validate_data"); }
内容的提问来源于stack exchange,提问作者Dmitriy Rudakov
相关产品推荐
相关产品推荐

