You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Apps Script网页表单输入校验异步响应延迟问题咨询

解决方案

问题根因

  • 前端异步逻辑错误:google.script.run 是异步API,发起请求后不会阻塞后续代码执行,原代码将校验、焦点跳转逻辑写在请求发起之后,还没等服务端返回结果就已经执行完成,导致校验完全失效
  • 服务端性能瓶颈:每次校验请求都重复读取电子表格整列数据,频繁IO操作导致接口返回耗时长达5秒

一、前端代码修复

核心修改点:

  1. 新增请求状态锁,避免重复提交
  2. 将所有校验判断、焦点跳转、表单提交逻辑全部迁移到异步回调onSuccess中执行
  3. 增加异常处理逻辑,覆盖请求失败场景

修改后的前端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);

二、服务端性能优化

核心修改点:

  1. 校验数据缓存到Apps Script缓存服务,避免每次请求都读取电子表格,大幅降低IO耗时
  2. 优化数据读取逻辑,减少getValues调用次数
  3. 修复原代码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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:21:03