异步函数

SpreadJS 支持创建异步自定义函数,可在不阻塞界面的情况下处理数据请求或耗时计算。该功能允许在计算期间显示默认值,支持多种重算模式,并能配合 REFRESH 函数灵活控制触发时机。

概述 本 Demo 展示了如何创建和使用异步自定义函数,实现延迟获取计算结果的功能。Demo 中定义了三个异步函数:ASUM(对参数求和并翻倍)、GetNumberFromServer(模拟从服务器获取随机数)、GetTimeFromServer(定时获取当前时间),并演示了不同的计算模式和触发方式。 实现思路 定义异步函数 ASUM,继承自 AsyncFunction,使用 setTimeout 模拟服务器端计算延迟 定义全局异步函数 GetNumberFromServer 和 GetTimeFromServer,模拟服务器数据获取 GetTimeFromServer 通过 evaluateMode 和 interval 方法设置定时计算模式 在工作表中使用 setFormula 设置包含异步函数的公式 使用 REFRESH 函数包装异步函数,控制不同的计算触发方式 代码解析 定义异步函数 这段代码定义了一个异步函数 ASUM,接受 1-255 个参数。defaultValue 方法返回"加载中…",在计算期间显示。evaluateAsync 方法使用 setTimeout 模拟 2 秒的服务器端延迟,计算完成后通过 context.setAsyncResult 返回结果(将参数求和后翻倍)。 设置定时计算模式 这段代码定义了定时获取时间的异步函数。evaluateMode 返回 2 表示按时间间隔计算,interval 返回 1000 表示每 1000 毫秒重新计算一次。函数每秒会自动刷新显示当前时间。 使用 REFRESH 函数控制计算模式 这段代码展示了 REFRESH 函数的三种计算模式: 第二个参数为 0 时,当引用的单元格 A10 值改变时触发重算 第二个参数为 1 时,函数只计算一次 第二个参数为 2 时,每隔指定时间间隔(第三个参数,单位毫秒)自动重算 运行效果 C2 单元格显示 "ASUM(A1,B1)" 的计算结果,初始显示 "加载中…",2 秒后显示计算结果(40) 编辑 C2 单元格的公式或更改 A1、B1 的值,可以看到异步函数重新计算的过程 C10 单元格直接使用 GetNumberFromServer 函数,在 A10 改变时重算 C11 单元格使用 Refresh 包装,模式为 0,在 A10 改变时重算 C12 单元格使用 Refresh 包装,模式为 1,只计算一次 C13 单元格使用 Refresh 包装,模式为 2,每 2 秒自动重算一次 C16 单元格显示当前时间,每 1 秒自动刷新 C19 单元格使用 GetTimeFromServer 函数,每 1 秒自动刷新显示当前时间 API 参考 AsyncFunction 构造函数 name: 函数名称 minArgs: 最小参数数量(可选) maxArgs: 最大参数数量(可选) description: 函数描述信息(可选) 关键方法 defaultValue(): 返回异步计算期间显示的默认值 evaluateAsync(context, ...args): 异步计算逻辑,参数中第一个参数为 context 对象,后续参数为函数参数 evaluateMode(): 返回计算模式(0: onRecalculation, 1: calculateOnce, 2: onInterval) interval(): 返回时间间隔(毫秒),仅在 evaluateMode 返回 2 时有效 setAsyncResult 方法 将异步计算结果设置到 CalcEngine,CalcEngine 使用此值重新计算包含此异步函数的单元格和所有依赖单元格。 addCustomFunction 方法 在工作表级别注册自定义函数。 defineGlobalCustomFunction 方法 全局注册自定义函数,可在所有工作表中使用。 REFRESH 函数 formula: 任意公式表达式 evaluateMode: 计算模式,对应 AsyncFunctionEvaluateMode 枚举(0: 依赖变化时重算, 1: 仅计算一次, 2: 按间隔重算) interval: 时间间隔(毫秒),仅在模式为 2 时有效
window.onload = function () { var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 2 }); initSpread(spread); }; function initSpread(spread) { var asyncSum = function () { }; asyncSum.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("ASUM", 1, 255); asyncSum.prototype.defaultValue = function () { return "加载中..."; }; asyncSum.prototype.evaluateAsync = function (context) { // use setTimeout to simulate server side evaluation // in read world it maybe an ajax post to server for evaluation var args = arguments; setTimeout(function () { var result = 0; for (var i = 1; i < args.length; i++) { result += args[i]; } result *= 2; context.setAsyncResult(result); }, 2000); }; var GetNumberFromServer = function () { }; GetNumberFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETNUMBERFROMSERVER", 1, 2); GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) { setTimeout(function () { var value = Math.random() + 1; context.setAsyncResult(value); }, 500); }; GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETNUMBERFROMSERVER", new GetNumberFromServer()); var GetTimeFromServer = function () { }; GetTimeFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETTIMEFROMSERVER"); GetTimeFromServer.prototype.evaluate = function (context) { setTimeout(function () { var date = new Date(); context.setAsyncResult(date); }, 500); }; GetTimeFromServer.prototype.evaluateMode = function () { return 2; }; GetTimeFromServer.prototype.interval = function () { return 1000; }; GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETTIMEFROMSERVER", new GetTimeFromServer()); var sheet = spread.sheets[0]; sheet.suspendPaint(); sheet.options.allowCellOverflow = true; sheet.setArray(0, 0, [[5, 15]]); sheet.addCustomFunction(new asyncSum()); sheet.setText(1, 1, 'ASUM(A1,B1)'); sheet.setText(2, 1, 'SUM(A1,B1)'); sheet.setFormula(1, 2, "ASUM(A1,B1)"); sheet.getCell(1, 2).foreColor("green"); sheet.setFormula(2, 2, "SUM(A1,B1)"); sheet.setValue(4, 0, "编辑 C2 单元格的公式或引用单元格的值,查看异步函数的工作方式。"); sheet.setValue(8, 0, '更改值'); sheet.setValue(8, 1, '公式'); sheet.setValue(8, 2, '结果'); sheet.setValue(8, 3, '备注'); sheet.setValue(9, 3, 'A10 单元格更改时'); sheet.setValue(10, 3, 'A10 单元格更改时'); sheet.setValue(11, 3, '计算一次'); sheet.setValue(12, 3, '每 2 秒计算一次'); sheet.setValue(9, 0, 1); sheet.setValue(9, 1, '=GetNumberFromServer(A10)'); sheet.setValue(10, 1, '=Refresh(GetNumberFromServer(A10), 0)'); sheet.setValue(11, 1, '=Refresh(GetNumberFromServer(A10), 1)'); sheet.setValue(12, 1, '=Refresh(GetNumberFromServer(A10), 2, 2000)'); sheet.setFormula(9, 2, '=GetNumberFromServer(A10)'); sheet.setFormula(10, 2, '=Refresh(GetNumberFromServer(A10), 0)'); sheet.setFormula(11, 2, '=Refresh(GetNumberFromServer(A10), 1)'); sheet.setFormula(12, 2, '=Refresh(GetNumberFromServer(A10), 2, 2000)'); sheet.getCell(12, 2).foreColor("green"); sheet.setColumnWidth(0, 100); sheet.setColumnWidth(1, 300); sheet.setColumnWidth(2, 200); sheet.setColumnWidth(3, 200); sheet.setValue(15, 1, "=Refresh(now(), 2, 1000)"); sheet.setValue(15, 3, "每 1 秒计算一次"); sheet.setFormula(15, 2, "=Refresh(now(), 2, 1000)"); sheet.getCell(15, 2).foreColor("green"); sheet.setValue(18, 1, "=GetTimeFromServer()"); sheet.setValue(18, 3, "每 1 秒计算一次"); sheet.setFormula(18, 2, "=GetTimeFromServer()"); sheet.getCell(18, 2).foreColor("green"); sheet.getCell(18, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.right); sheet.resumePaint(); }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta name="spreadjs culture" content="zh-cn" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div class="sample-tutorial"> <div id="ss" style="width:100%; height:100%;"></div> </div> </body> </html>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }