Async Function

SpreadJS provides AsyncFunction to support evaluating asynchronous values; it is used for scenarios in which the process could not get the calculated value immediately, such as server side calculation.

Description
app.component.ts
index.html
app.component.html
styles.css
Copy to CodeMine

Create a class by inheriting GC.Spread.CalcEngine.Functions.AsyncFunction.

Create a display string with the defaultValue method.

Use the evaluateAsync method to return the result. Set the result with context.setAsyncResult.

    var asum = function () {};
    asum.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction('ASUM', 1, 255);
    asum.prototype.defaultValue = function () { return 'Loading...'; };
    asum.prototype.evaluateAsync = function (context, arg) {
      // do evaluate on server to get result ...
        fetch("data").then(function(response){
            context.setAsyncResult(response.text());
        })
    };

Use AsyncFunctionEvaluateMode to represent the async function evaluate mode, There are three modes:

    Functions.AsyncFunctionEvaluateMode = {
        onRecalculation: 0,
        calculateOnce: 1,
        onInterval: 2
  • 0 means the AsyncFunction should recalculate when the cell needs to recalculate. For example: B1='MyAsync1(A1) + MyAsync2(A2)'. When the value of A1 changes, the formula of B1 will recalculate, so both 'MyAsync1(A1)' and 'MyAsync2(A2)' will recalculate.
  • 1 means the formula in the REFRESH function only evaluates once.
  • 2 means the formula in the REFRESH function will recalculate based on an interval.

Set the refresh method

    refresh(formula, evaluateMode, interval)
  • Can use any formula on the first argument of the REFRESH function.
  • The evaluateMode value should be one of the GC.Spread.CalcEngine.Functions.AsyncFunctionEvaluateMode enumeration values.
  • The interval can only take effect when the evaluateMode is 2.
  • Normally, use the REFRESH function as the outermost function.
  • When setting the AsyncFunction inside the REFRESH function, it uses the REFRESH function settings. The AsyncFunction settings do not take effect.

Use the addCustomFunction method to add the defined async function, then use it in a formula, such as by using the setFormula method

    sheet.addCustomFunction(new asum());
    sheet.setFormula(1, 1, 'ASUM(A1,B1)');
Create a class by inheriting GC.Spread.CalcEngine.Functions.AsyncFunction. Create a display string with the defaultValue method. Use the evaluateAsync method to return the result. Set the result with context.setAsyncResult. Use AsyncFunctionEvaluateMode to represent the async function evaluate mode, There are three modes: 0 means the AsyncFunction should recalculate when the cell needs to recalculate. For example: B1='MyAsync1(A1) + MyAsync2(A2)'. When the value of A1 changes, the formula of B1 will recalculate, so both 'MyAsync1(A1)' and 'MyAsync2(A2)' will recalculate. 1 means the formula in the REFRESH function only evaluates once. 2 means the formula in the REFRESH function will recalculate based on an interval. Set the refresh method Can use any formula on the first argument of the REFRESH function. The evaluateMode value should be one of the GC.Spread.CalcEngine.Functions.AsyncFunctionEvaluateMode enumeration values. The interval can only take effect when the evaluateMode is 2. Normally, use the REFRESH function as the outermost function. When setting the AsyncFunction inside the REFRESH function, it uses the REFRESH function settings. The AsyncFunction settings do not take effect. Use the addCustomFunction method to add the defined async function, then use it in a formula, such as by using the setFormula method
import { Component, NgModule, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { SpreadSheetsModule } from '@mescius/spread-sheets-angular'; import GC from '@mescius/spread-sheets'; import './styles.css'; @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { spread: GC.Spread.Sheets.Workbook; hostStyle = { width: '100%', height: '100%', overflow: 'hidden', float: 'left' }; constructor() { } initSpread($event: any) { this.spread = $event.spread; let spread = this.spread; let asyncSum = function () { }; asyncSum.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("ASUM", 1, 255); asyncSum.prototype.defaultValue = function () { return "Loading..."; }; asyncSum.prototype.evaluateAsync = function (context) { // use setTimeout to simulate server side evaluation // in read world it maybe an ajax post to server for evaluation let args = arguments; setTimeout(function () { let result = 0; for (let i = 1; i < args.length; i++) { result += args[i]; } result *= 2; context.setAsyncResult(result); }, 2000); }; let GetNumberFromServer = function () { }; GetNumberFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETNUMBERFROMSERVER", 1, 2); GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) { setTimeout(function () { let value = Math.random() + 1; context.setAsyncResult(value); }, 500); }; GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETNUMBERFROMSERVER", new GetNumberFromServer()); let GetTimeFromServer = function () { }; GetTimeFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETTIMEFROMSERVER"); GetTimeFromServer.prototype.evaluate = function (context) { setTimeout(function () { let 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()); let 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, "Edit the formula of C2 or referenced cell' value to see how async function works."); sheet.setValue(8, 0, 'CHANGEVALUE'); sheet.setValue(8, 1, 'FORMULA'); sheet.setValue(8, 2, 'RESULT'); sheet.setValue(8, 3, 'COMMENTS'); sheet.setValue(9, 3, 'On A10 changed'); sheet.setValue(10, 3, 'On A10 changed'); sheet.setValue(11, 3, 'Evaluate once'); sheet.setValue(12, 3, 'Every 2 seconds'); 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, "Every 1 second"); sheet.setFormula(15, 2, "=Refresh(now(), 2, 1000)"); sheet.getCell(15, 2).foreColor("green"); sheet.setValue(18, 1, "=GetTimeFromServer()"); sheet.setValue(18, 3, "Every 1 second"); sheet.setFormula(18, 2, "=GetTimeFromServer()"); sheet.getCell(18, 2).foreColor("green"); sheet.getCell(18, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.right); sheet.resumePaint(); } } @NgModule({ imports: [BrowserModule, SpreadSheetsModule], declarations: [AppComponent], exports: [AppComponent], bootstrap: [AppComponent] }) export class AppModule {} enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/angular/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <!-- Polyfills --> <script src="$DEMOROOT$/en/angular/node_modules/core-js/client/shim.min.js"></script> <script src="$DEMOROOT$/en/angular/node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="$DEMOROOT$/en/angular/node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.import('@angular/compiler'); System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('$DEMOROOT$/en/lib/angular/license.ts'); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div class="sample-tutorial"> <gc-spread-sheets [hostStyle]="hostStyle" (workbookInitialized)="initSpread($event)"> <gc-worksheet> </gc-worksheet> <gc-worksheet> </gc-worksheet> </gc-spread-sheets> </div>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }
(function (global) { System.config({ transpiler: 'ts', typescriptOptions: { tsconfig: true }, meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'core-js': 'npm:core-js/client/shim.min.js', 'zone': 'npm:zone.js/fesm2015/zone.min.js', 'rxjs': 'npm:rxjs/dist/bundles/rxjs.umd.min.js', '@angular/core': 'npm:@angular/core/fesm2022', '@angular/common': 'npm:@angular/common/fesm2022/common.mjs', '@angular/compiler': 'npm:@angular/compiler/fesm2022/compiler.mjs', '@angular/platform-browser': 'npm:@angular/platform-browser/fesm2022/platform-browser.mjs', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/fesm2022/platform-browser-dynamic.mjs', '@angular/common/http': 'npm:@angular/common/fesm2022/http.mjs', '@angular/router': 'npm:@angular/router/fesm2022/router.mjs', '@angular/forms': 'npm:@angular/forms/fesm2022/forms.mjs', 'jszip': 'npm:jszip/dist/jszip.min.js', 'typescript': 'npm:typescript/lib/typescript.js', 'ts': './plugin.js', 'tslib':'npm:tslib/tslib.js', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js', '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-angular': 'npm:@mescius/spread-sheets-angular/fesm2020/mescius-spread-sheets-angular.mjs', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'ts' }, rxjs: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, "node_modules/@angular": { defaultExtension: 'mjs' }, "@mescius/spread-sheets-angular": { defaultExtension: 'mjs' }, '@angular/core': { defaultExtension: 'mjs', main: 'core.mjs' } } }); })(this);