找回密码
 立即注册

QQ登录

只需一步,快速开始

Universail
金牌服务用户   /  发表于:2023-12-22 12:39  /   查看:2942  /  回复:31
本帖最后由 Richard.Huang 于 2023-12-25 11:55 编辑

产品:SpreadJS
版本:V16.2.6
问题编号:SJS-22025

自定义的异步公式:MyFormula,这个异步公式的evaluateAsync中会请求一个“注册”函数:RegFn,该函数返回一个Promise,但是不会立刻为公式提供运算结果:

MyFormula.evaluateAsync = async function(ctx){     const key = `${ctx.id}-${ctx.row}-${ctx.col}`;//公式唯一标记     const req = {key,param:{/*其他请求参数*/}}     const res = await RegFn(req)           context.setAsyncResult(res);}
const reqMap = {}const promiseMap = {}
RegFn函数定义:function RegFn(req){    return new Promise((resolve, reject) => {        reqMap[req.key] = req //收集所有公式的请求参数        promiseMap[req.key] = {resolve, reject}  //保存resolve,以便后面调完后端以后,把结果提交给公式    });

}
手动执行所有公式:async function exe(){     const res =   await api.xxx.post("url", reqMap)  //请求后端,一次性执行所有公式,    res.data.forEach((v)=>{         promiseMap[v.key].resolve(v.value)  //后端返回的结果中包含公式的唯一key,用之前保存好的promiseMap获取resolve,将结果返回给公式    })}

以上内容,已经实现,达到的预期效果,但是出现了如下问题:

如果单元格中只使用1-2个MyFormula公式,是没问题的,但是如果超过2个MyFormula公式,第3个MyFormula公式就不会运算了,表现为在第二个MyFormula公式处短路。
比如:MyFormula() - MyFormula() + MyFormula() - MyFormula()  .....
上述表达式中,前面2个MyFormula公式会触发MyFormula.evaluateAsync,但是第3个及之后的MyFormula公式,则不会触发MyFormula.evaluateAsync。
反复执行exe()函数多次后,最终所有的公式都会触发,但是这个不是预期效果,不可能让用户一遍遍执行exe以获取最终结果。

需求:所有公式编辑完成以后,都执行一次,即调用一次MyFormula.evaluateAsync,不要出现短路现象。

PS:上次咨询过:如何避免几百上千个公式瞬间调用后端几百上千次?没有得到解决方案,这个是我能想到的解决方案,可以实现一次请求,计算所有公式的结果,然后返回给公式使用。








31 个回复

倒序浏览
Richard.HuangSpreadJS 开发认证
超级版主   /  发表于:2023-12-22 13:59:51
沙发
您好,我按照您的描述在SpreadJSV16.2.6中进行多个自定义异步函数的计算,但是并没有复现您所描述的问题,仅仅只是多个自定义异步函数组合计算时会因为延迟的原因计算有些慢。您也可以在这篇学习指南的demo上进行测试:https://demo.grapecity.com.cn/sp ... ync-function/purejs
asyncfunction.gif460587768.png
回复 使用道具 举报
WilliamChang
葡萄城公司职员   /  发表于:2023-12-22 14:19:36
板凳
你好,猜测原因是MyFormula 的默认值不是数字,这就导致了MyFormula() - MyFormula() + MyFormula() - MyFormula()  ..... 前两个结点计算之后出现了#VALUE!,然后后面的就不需要evaluateAsync了。然后前两个结点的结果设置之后再继续计算这个公式。

情况和这个很像 https://gcdn.grapecity.com.cn/fo ... read&tid=175545
回复 使用道具 举报
Richard.HuangSpreadJS 开发认证
超级版主   /  发表于:2023-12-22 14:59:50
地板
WilliamChang 发表于 2023-12-22 14:19
你好,猜测原因是MyFormula 的默认值不是数字,这就导致了MyFormula() - MyFormula() + MyFormula() - MyFo ...

回复 使用道具 举报
Universail
金牌服务用户   /  发表于:2023-12-22 19:32:44
5#
WilliamChang 发表于 2023-12-22 14:19
你好,猜测原因是MyFormula 的默认值不是数字,这就导致了MyFormula() - MyFormula() + MyFormula() - MyFo ...

是同样的意思。但是默认值我一直设置的都是0:

      public defaultValue() {
        return 0;
      }

回复 使用道具 举报
Universail
金牌服务用户   /  发表于:2023-12-22 19:52:42
6#
Richard.Huang 发表于 2023-12-22 13:59
您好,我按照您的描述在SpreadJSV16.2.6中进行多个自定义异步函数的计算,但是并没有复现您所描述的问题, ...

测试的方法不对,异步函数里“只注册,不计算”,如果直接计算,那是没问题的,但是我需要把所有公式的请求收集起来,一次性提交给后端(不能一个公式一个公式提交,这会造成很大并发)。

所以当多个公式运算的时候,由于短路效果,导致后面的公式不能注册,所以后端请求的结果里就没有对应的数据了
回复 使用道具 举报
Universail
金牌服务用户   /  发表于:2023-12-22 20:17:09
7#
Richard.Huang 发表于 2023-12-22 13:59
您好,我按照您的描述在SpreadJSV16.2.6中进行多个自定义异步函数的计算,但是并没有复现您所描述的问题, ...
  1. window.onload = function () {
  2.     var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), {sheetCount: 2});
  3.     initSpread(spread);
  4. };

  5. const reqMap = {}
  6. const pMap = {}

  7. function RegFn(key){
  8.     return new Promise((resolve)=>{
  9.        pMap[key] = {resolve}
  10.     })
  11. }

  12. function Execute1(){
  13.     for (let k in pMap){
  14.         pMap[k].resolve(100)
  15.     }
  16. }

  17. function initSpread(spread) {
  18.     const asyncExe = function(){}
  19.     asyncExe.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("AEXE", 1, 1);
  20.     asyncExe.prototype.defaultValue = function () {
  21.             return 0
  22.     };
  23.     asyncExe.prototype.evaluateAsync =async function (context) {
  24.         await Execute1()
  25.         context.setAsyncResult("这个运算结果不是重点,只是为了触发函数");
  26.     };


  27.     var asyncSum = function () {
  28.     };
  29.     asyncSum.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("ASUM", 1, 255);
  30.     asyncSum.prototype.defaultValue = function () {
  31.         return 0
  32.     };
  33.     asyncSum.prototype.evaluateAsync =async function (context) {
  34.         // use setTimeout to simulate server side evaluation
  35.         // in read world it maybe an ajax post to server for evaluation

  36.         const key = `${context.id}-${context.row}-${context.col}`;
  37.         alert('注册异步任务:'+context.id)//这里可以观察到单元格C2中第三个公式没有执行
  38.         const res = await RegFn(key)
  39.         context.setAsyncResult(res);
  40.         alert("此时才返回结果")
  41.     };

  42.     var GetNumberFromServer = function () {
  43.     };
  44.     GetNumberFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETNUMBERFROMSERVER", 1, 2);
  45.     GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) {
  46.         setTimeout(function () {
  47.             var value = Math.random() + 1;
  48.             context.setAsyncResult(value);
  49.         }, 500);
  50.     };
  51.     GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETNUMBERFROMSERVER", new GetNumberFromServer());

  52.     var GetTimeFromServer = function () {
  53.     };
  54.     GetTimeFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETTIMEFROMSERVER");
  55.     GetTimeFromServer.prototype.evaluate = function (context) {
  56.         setTimeout(function () {
  57.             var date = new Date();
  58.             context.setAsyncResult(date);
  59.         }, 500);
  60.     };
  61.     GetTimeFromServer.prototype.evaluateMode = function () {
  62.         return 2;
  63.     };
  64.     GetTimeFromServer.prototype.interval = function () {
  65.         return 1000;
  66.     };
  67.     GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETTIMEFROMSERVER", new GetTimeFromServer());

  68.     var sheet = spread.sheets[0];
  69.     sheet.suspendPaint();
  70.     sheet.options.allowCellOverflow = true;

  71.     sheet.setArray(0, 0, [[5, 15]]);
  72.     sheet.addCustomFunction(new asyncSum());
  73.         sheet.addCustomFunction(new asyncExe());

  74.     sheet.setText(1, 1, 'ASUM(A1,B1)');
  75.     sheet.setText(2, 1, 'SUM(A1,B1)');

  76.     sheet.setFormula(1, 2, "ASUM(A1,B1)+ASUM(A1,B1)+ASUM(A1,B1)");
  77.     sheet.getCell(1, 2).foreColor("green");
  78.     sheet.setFormula(2, 2, "SUM(A1,B1)");

  79.     sheet.setValue(4, 0, "Edit the formula of C2 or referenced cell' value to see how async function works.");

  80.     sheet.setValue(8, 0, 'CHANGEVALUE');
  81.     sheet.setValue(8, 1, 'FORMULA');
  82.     sheet.setValue(8, 2, 'RESULT');
  83.     sheet.setValue(8, 3, 'COMMENTS');
  84.     sheet.setValue(9, 3, 'On A10 changed');
  85.     sheet.setValue(10, 3, 'On A10 changed');
  86.     sheet.setValue(11, 3, 'Evaluate once');
  87.     sheet.setValue(12, 3, 'Every 2 seconds');
  88.     sheet.setValue(9, 0, 1);
  89.     sheet.setValue(9, 1, '=GetNumberFromServer(A10)');
  90.     sheet.setValue(10, 1, '=Refresh(GetNumberFromServer(A10), 0)');
  91.     sheet.setValue(11, 1, '=Refresh(GetNumberFromServer(A10), 1)');
  92.     sheet.setValue(12, 1, '=Refresh(GetNumberFromServer(A10), 2, 2000)');
  93.     sheet.setFormula(9, 2, '=GetNumberFromServer(A10)');
  94.     sheet.setFormula(10, 2, '=Refresh(GetNumberFromServer(A10), 0)');
  95.     sheet.setFormula(11, 2, '=Refresh(GetNumberFromServer(A10), 1)');
  96.     sheet.setFormula(12, 2, '=Refresh(GetNumberFromServer(A10), 2, 2000)');
  97.     sheet.getCell(12, 2).foreColor("green");
  98.     sheet.setColumnWidth(0, 100);
  99.     sheet.setColumnWidth(1, 300);
  100.     sheet.setColumnWidth(2, 200);
  101.     sheet.setColumnWidth(3, 200);

  102.     sheet.setValue(15, 1, "=Refresh(now(), 2, 1000)");
  103.     sheet.setValue(15, 3, "Every 1 second");
  104.     sheet.setFormula(15, 2, "=Refresh(now(), 2, 1000)");
  105.     sheet.getCell(15, 2).foreColor("green");

  106.     sheet.setValue(18, 1, "=GetTimeFromServer()");
  107.     sheet.setValue(18, 3, "Every 1 second");
  108.     sheet.setFormula(18, 2, "=GetTimeFromServer()");
  109.     sheet.getCell(18, 2).foreColor("green");

  110.     sheet.getCell(18, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.right);
  111.     sheet.resumePaint();
  112. }
复制代码
回复 使用道具 举报
Universail
金牌服务用户   /  发表于:2023-12-22 20:18:25
8#
Richard.Huang 发表于 2023-12-22 13:59
您好,我按照您的描述在SpreadJSV16.2.6中进行多个自定义异步函数的计算,但是并没有复现您所描述的问题, ...

用我提供的代码测试,可以重现问题,本来想做个按钮来触发异步执行,但是不会做,所以你就用AEXE这个公式来触发执行吧
回复 使用道具 举报
Richard.HuangSpreadJS 开发认证
超级版主   /  发表于:2023-12-25 11:40:11
9#
本帖最后由 Richard.Huang 于 2023-12-25 11:54 编辑
Universail 发表于 2023-12-22 20:18
用我提供的代码测试,可以重现问题,本来想做个按钮来触发异步执行,但是不会做,所以你就用AEXE这个公式 ...

感谢您提供的测试代码,我按照您的代码确实复现了您所描述的问题。对此我们将进行深度调研,后续有相关调研结果,我会第一时间在本贴中进行回复。问题编号:SJS-22025
回复 使用道具 举报
Universail
金牌服务用户   /  发表于:2023-12-25 12:07:39
10#
Richard.Huang 发表于 2023-12-25 11:40
感谢您提供的测试代码,我按照您的代码确实复现了您所描述的问题。对此我们将进行深度调研,后续有相关调 ...

好的,等你好消息
回复 使用道具 举报
1234下一页
您需要登录后才可以回帖 登录 | 立即注册
返回顶部