动态

详情 返回 返回

用 evaluateFormulaAsync 實現高效異步公式計算 - 动态 详情

在表格應用開發中,公式計算是核心能力之一。傳統同步公式計算在處理耗時任務(如複雜邏輯運算、異步API交互)時,容易導致用户界面(UI)凍結,嚴重影響用户體驗。為解決這一痛點,SpreadJS V18.2 正式推出 evaluateFormulaAsync 方法,提供異步公式計算能力,兼顧計算效率與交互流暢性。本文將從特性概述、注意事項、適用場景、API細節及實戰示例五個維度,全面解析該新特性。

image.png

一、特性概述

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. 方法簽名

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秒的服務端延遲,計算等待時顯示“加載中...”:

// 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):

// 調用異步公式計算方法
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秒):

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秒):

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 數組函數),可混合使用異步與同步邏輯:

// 公式:用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 會自動讀取單元格上下文:

// 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 創建時間以模擬服務器端計算

user avatar huikaichedemianbao 头像 woniuseo 头像 pannideniupai 头像 kinglisky 头像 cloudyttt 头像 winnie_5e6e740266348 头像 jrainlau 头像 emonzan 头像 codemz 头像 c_z7wgq 头像 liuliuliuliuliudaling 头像
点赞 11 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.