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

一、特性概述
evaluateFormulaAsync 是 SpreadJS 计算引擎(GC.Spread.Sheets.CalcEngine)新增的异步方法,核心作用是计算指定的公式字符串,并返回包含计算结果的 Promise 对象。
与传统同步计算相比,该方法的核心优势在于:
- 非阻塞UI:计算过程在异步线程中执行,不会冻结页面,用户可正常操作界面;
- 支持异步逻辑:无缝集成自定义异步函数、异步API或数据源,拓展公式计算的适用场景;
- Promise 化结果:通过 Promise 的 then/catch 机制,简化异步结果处理与错误捕获流程。
二、核心注意事项
在使用 evaluateFormulaAsync 前,需明确以下限制条件,避免开发踩坑:
- 不支持的特性:
- 禁用 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 的核心新特性,为表格应用开发带来三大关键价值:
- 提升用户体验:异步计算避免UI冻结,用户可在计算过程中继续操作(如滚动表格、编辑单元格);
- 拓展业务场景:支持异步自定义函数、API集成,满足"实时数据计算""耗时任务处理"等复杂需求;
- 兼容与灵活:无缝兼容现有同步公式与A1引用样式,无需重构历史代码,降低迁移成本。