用 evaluateFormulaAsync 实现高效异步公式计算

在表格应用开发中,公式计算是核心能力之一。传统同步公式计算在处理耗时任务(如复杂逻辑运算、异步API交互)时,容易导致用户界面(UI)冻结,严重影响用户体验。为解决这一痛点,SpreadJS V18.2 正式推出 evaluateFormulaAsync 方法,提供异步公式计算能力,兼顾计算效率与交互流畅性。本文将从特性概述、注意事项、适用场景、API细节及实战示例五个维度,全面解析该新特性。

一、特性概述

evaluateFormulaAsync 是 SpreadJS 计算引擎(GC.Spread.Sheets.CalcEngine)新增的异步方法,核心作用是计算指定的公式字符串,并返回包含计算结果的 Promise 对象

与传统同步计算相比,该方法的核心优势在于:

  • 非阻塞UI:计算过程在异步线程中执行,不会冻结页面,用户可正常操作界面;
  • 支持异步逻辑:无缝集成自定义异步函数、异步API或数据源,拓展公式计算的适用场景;
  • Promise 化结果:通过 Promise 的 then/catch 机制,简化异步结果处理与错误捕获流程。

二、核心注意事项

在使用 evaluateFormulaAsync 前,需明确以下限制条件,避免开发踩坑:

  1. 不支持的特性
  • 禁用 AsyncFunctionEvaluateMode.onInterval(异步函数间隔计算模式);
  • 禁用 REFRESH 函数,该函数的实时刷新逻辑与异步计算机制冲突。

​ 2.公式引用样式 :仅支持 A1 引用样式(如 A1:B2),不支持 R1C1 样式(如 R1C1:R2C2)。

​ 3.异步结果处理 :若多次调用 context.setAsyncResult(用于设置异步函数结果),Promise 仅会在第一次调用时解析结果,后续调用无效。

三、适用场景

evaluateFormulaAsync 并非替代所有同步计算,而是针对特定场景优化。以下三类场景最能体现其价值:

1. 自定义异步函数场景

当业务需要自定义包含异步逻辑的函数(如延迟计算、异步数据处理)时,可通过该方法触发计算。例如:实现"异步求和""异步数据统计"等自定义函数,计算等待期间显示"加载中"提示。

2. 异步API/数据源集成场景

若公式计算依赖外部异步API(如后端接口获取实时数据、第三方服务查询),evaluateFormulaAsync 可避免UI阻塞。例如:公式中调用"异步获取实时汇率"的API,计算跨国交易金额。

3. 耗时计算场景

对于复杂的数学运算、大数据量统计(如百万级数据求和、多维度透视分析),同步计算会占用主线程导致UI卡顿,而异步计算可在后台执行,不影响用户操作。

四、API 详细解析

evaluateFormulaAsync 方法的定义、参数及返回值设计简洁且灵活,适配不同计算场景。

1. 方法签名

JavaScript 复制代码
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(
  context: Object, 
  formula: string, 
  baseRow?: number, 
  baseColumn?: number
): Promise<any>

2. 参数详解

参数名 类型 是否必选 说明
context Object 计算上下文对象,绝大多数场景下需传入活动工作表实例(GC.Spread.Sheets.Worksheet),包含公式计算所需的单元格数据、自定义函数注册信息等。
formula string 待计算的公式字符串,需符合 SpreadJS 公式语法,且仅支持 A1 引用样式。
baseRow number 公式基准行索引(从0开始),仅在单元格上下文外计算时使用,用于处理公式中的相对引用(如公式 B1 若 baseRow=2,则相对引用变为 B3)。
baseColumn number 公式基准列索引(从0开始),作用与 baseRow 类似,用于处理相对引用的列偏移。

3. 返回值

返回一个 Promise 对象:

  • 计算成功时,Promise 解析为公式的最终计算结果(可能是数字、字符串、数组等,取决于公式逻辑);
  • 计算失败时(如公式语法错误、异步函数异常),Promise 会 reject 并抛出错误信息,需通过 catch 捕获。

五、实战示例

为帮助开发者快速上手,以下通过5个核心示例,覆盖"自定义异步函数""基础异步计算""并行/串行执行""常规公式兼容""单元格引用计算"场景。

前置准备:定义自定义异步函数 ASUM

首先注册一个异步求和函数 ASUM,使用 setTimeout 模拟2秒的服务端延迟,计算等待时显示"加载中...":

JavaScript 复制代码
// 1. 定义异步函数构造函数
var AsyncSum = function () {};

// 2. 继承 AsyncFunction,指定函数名"ASUM",参数数量范围(1~255个)
AsyncSum.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("ASUM", 1, 255);

// 3. 计算等待期间的默认显示值(提升用户体验)
AsyncSum.prototype.defaultValue = function () {
  return "加载中...";
};

// 4. 核心异步计算逻辑
AsyncSum.prototype.evaluateAsync = function (context) {
  // 收集函数参数(索引0为context,实际参数从索引1开始)
  const args = Array.from(arguments).slice(1);
  
  // 模拟2秒服务端延迟
  setTimeout(() => {
    // 计算所有参数的和
    const sum = args.reduce((acc, curr) => acc + (curr || 0), 0);
    // 设置最终结果(示例:将求和结果翻倍)
    context.setAsyncResult(sum * 2);
  }, 2000);
};

// 5. 全局注册自定义函数(所有工作表可调用)
GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("ASUM", new AsyncSum());

// 假设已初始化工作表实例
const spread = new GC.Spread.Sheets.Workbook("spread-container");
const sheet = spread.getActiveSheet(); // 活动工作表(作为计算上下文)

示例1:基础异步计算

调用 ASUM(2, 2),验证异步计算结果(约2秒后返回 (2+2)*2=8):

JavaScript 复制代码
// 调用异步公式计算方法
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "ASUM(2, 2)", 0, 0)
  .then((result) => {
    console.log("ASUM(2, 2) 结果:", result); // 输出:ASUM(2, 2) 结果:8
  })
  .catch((error) => {
    console.error("计算错误:", error); // 捕获公式语法错误、异步逻辑异常等
  });

示例2:并行执行异步函数

计算 ASUM(2, 2) + ASUM(2, 2),两个 ASUM 并行执行(总耗时约2秒,而非4秒):

JavaScript 复制代码
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "ASUM(2, 2) + ASUM(2, 2)", 0, 0)
  .then((result) => {
    // 两个ASUM分别返回8,总和为16
    console.log("ASUM(2, 2) + ASUM(2, 2) 结果:", result); // 输出:16
  })
  .catch((error) => {
    console.error("计算错误:", error);
  });

原理:SpreadJS 异步计算引擎会并行调度多个独立的异步函数,无需等待前一个完成,大幅缩短总耗时。

示例3:串行(嵌套)执行异步函数

计算 ASUM(ASUM(2, 2), 2),内层 ASUM 执行完成后再执行外层(总耗时约4秒):

JavaScript 复制代码
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "ASUM(ASUM(2, 2), 2)", 0, 0)
  .then((result) => {
    // 1. 内层 ASUM(2,2) → 8(耗时2秒)
    // 2. 外层 ASUM(8,2) → (8+2)*2=20(再耗时2秒)
    console.log("ASUM(ASUM(2, 2), 2) 结果:", result); // 输出:20
  })
  .catch((error) => {
    console.error("计算错误:", error);
  });

原理:嵌套异步函数需等待内层结果返回后,才能执行外层计算,因此总耗时为各层耗时之和。

示例4:计算常规(同步)公式

evaluateFormulaAsync 不仅支持异步函数,还兼容常规同步公式(如 VSTACK 数组函数),可混合使用异步与同步逻辑:

JavaScript 复制代码
// 公式:用VSTACK拼接两个ASUM的结果
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "VSTACK(ASUM(2, 2), ASUM(2))", 0, 0)
  .then((result) => {
    // ASUM(2,2)→8,ASUM(2)→4,VSTACK将结果拼接为二维数组
    console.log("VSTACK 结果:", result); // 输出:[[8], [4]]
  })
  .catch((error) => {
    console.error("计算错误:", error);
  });

示例5:计算单元格/区域引用

若公式包含单元格引用(如 A1 B2),需确保工作表中已有对应数据,evaluateFormulaAsync 会自动读取单元格上下文:

JavaScript 复制代码
// 1. 先向单元格写入数据
sheet.setValue(0, 0, 23); // A1 = 23

// 2. 计算公式 "1 + A1"(即 1 + 23)
GC.Spread.Sheets.CalcEngine.evaluateFormulaAsync(sheet, "1 + A1", 0, 0)
  .then((result) => {
    console.log("常规公式结果:", result); // 输出:24
  })
  .catch((error) => {
    console.error("计算错误:", error);
  });

六、特性价值总结

evaluateFormulaAsync 作为 SpreadJS V18.2 的核心新特性,为表格应用开发带来三大关键价值:

  1. 提升用户体验:异步计算避免UI冻结,用户可在计算过程中继续操作(如滚动表格、编辑单元格);
  2. 拓展业务场景:支持异步自定义函数、API集成,满足"实时数据计算""耗时任务处理"等复杂需求;
  3. 兼容与灵活:无缝兼容现有同步公式与A1引用样式,无需重构历史代码,降低迁移成本。

扩展链接

异步函数AsyncFunction 与 AsyncEvaluateContext 创建时间以模拟服务器端计算

相关推荐
谢小飞3 分钟前
Echarts高级柱状图开发:渐变与3D效果实现
前端·echarts
FogLetter6 分钟前
Vite vs Webpack:前端构建工具的双雄对决
前端·面试·vite
tianchang8 分钟前
JS 排序神器 sort 的正确打开方式
前端·javascript·算法
怪可爱的地球人11 分钟前
ts的类型兼容性
前端
方圆fy18 分钟前
探秘Object.prototype.toString(): 揭开 JavaScript 深层数据类型的神秘面纱
前端
FliPPeDround21 分钟前
🚀 定义即路由:definePage宏如何让uni-app路由配置原地起飞?
前端·vue.js·uni-app
怪可爱的地球人22 分钟前
ts的类型推论
前端
林太白28 分钟前
动态角色权限和动态权限到底API是怎么做的你懂了吗
前端·后端·node.js
每一天,每一步33 分钟前
React页面使用ant design Spin加载遮罩指示符自定义成进度条的形式
前端·react.js·前端框架
moyu8444 分钟前
Pinia 状态管理:现代 Vue 应用的优雅解决方案
前端