找回密码
 立即注册

QQ登录

只需一步,快速开始

dexteryao 讲师达人认证 悬赏达人认证 SpreadJS 开发认证
超级版主   /  发表于:2021-4-25 10:56  /   查看:3590  /  回复:6
本帖最后由 dexteryao 于 2021-4-25 10:56 编辑

SpreadJS提供了异步函数的功能,可以让函数通过调用API异步获取数据。但是当页面上使用的异步函数较多,刷新计算时会同时发生大量的网络请求,不仅给服务器造成压力,也会由于异步函数的同时更新造成页面的频繁刷新,影响用户体验。

为了解决这个问题,我们可以采用请求堆栈的方式,收集函数请求,统一发送网络请求并一次更新。
具体更新时机机制可以根据我们业务需求决定,下面以定时请求为例说明具体实现。
当第一个请求发生1秒后,发起一次网络请求。1秒内有其他请求进入就收集进堆栈,后续请求放入下一个堆栈,以此类推。

下面代码是一个普通异步函数GETNUMBERFROMSERVER的实现,调用getData接口(模拟接口直接返回arg1数据,未做其他处理),传递参数,获取显示内容并展示在单元格。
  1.     var GetNumberFromServer = function () {
  2.     };
  3.     GetNumberFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETNUMBERFROMSERVER", 1, 2);
  4.     GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) {
  5.     fetch("/spread/getData?data="+arg1)
  6.     .then(function(response) {
  7.         return response.text();
  8.     })
  9.     .then(function(text) {
  10.         context.setAsyncResult(text);
  11.     });
  12.     };
  13.     GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETNUMBERFROMSERVER", new GetNumberFromServer());
复制代码

为了减少请求,我们使用一个缓存对象存放请求数据,定时调用接口处理
  1. let callStack = {}; //收集请求数据
  2. let callingStack = {}; //缓存正在请求中的数据信息
  3. let callStackCount = 0; //请求数量,当作请求ID,用于区分请求内容
  4. let timingId = 0; //用于判断当前是否有定时器等待请求中
复制代码

定义请求方法,代替再函数中直接调用API接口

  1. // data 请求数据
  2. // context 异步函数context, 网络请求结束后回调时使用
  3. // callback 回调函数
  4. function stackCall(data, context, callback){
  5.     let id = callStackCount++;
  6.     callStack[id] = {};
  7.     callStack[id].data = data;
  8.     callStack[id].context = context;
  9.     callStack[id].callback = callback;

  10.     if(timingId === 0){ // 同时只有一个定时器
  11.         timingId = setTimeout(function(){
  12.             callingStack = callStack;
  13.             callStack = {};

  14.             let newData = "" //合并请求数据,根据实际业务情况整理
  15.             for(let cId in callingStack){
  16.                 newData += (cId + "," + callingStack[cId].data + ";");
  17.             }

  18.             // 发送请求,这里模拟数据,发送什么返回什么
  19.             fetch("/spread/getData?data=" + newData)
  20.                   .then(function(response) {
  21.                     return response.text();
  22.                   })
  23.                   .then(function(text) {
  24.                     let resData = newData.split(";");
  25.                     let spread = designer.getWorkbook();
  26.                     spread.suspendPaint(); //暂定页面绘制

  27.                     //解析返回的数据
  28.                     for(let resId in resData){
  29.                         if(resData[resId]){
  30.                             let ress = resData[resId].split(",");
  31.                             // 根据Id,获取函数的context,调用callback回调
  32.                             callingStack[ress[0]].callback.call(null, callingStack[ress[0]].context, ress[1])
  33.                         }
  34.                     }
  35.                     spread.resumePaint(); //重启统一绘制
  36.                 timingId = 0;
  37.             });
  38.         }, 1000)
  39.     }
  40. }
复制代码

更新异步函数实现方式,函数中调用stackCall堆栈函数,批量调用成功后执行callback回调中的setAsyncResult方法。
  1. GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) {
  2.     stackCall(arg1, context, function(context, text){
  3.         context.setAsyncResult(text);
  4.     })
  5.     };
复制代码




通过以上实现,当页面有大量异步请求时会统一处理,一次刷新。
另外还可以使用doNotRecalculateAfterLoad导入选项,在首次加载时不计算,使用json中原始值,以及calcOnDemand开启按需计算。
这两个option可以根据您的实际需求设置。
  1. json.calcOnDemand = true;
  2. spread.fromJSON(json, { doNotRecalculateAfterLoad: true });
复制代码


6 个回复

倒序浏览
athenadeveloper
注册会员   /  发表于:2022-10-14 11:15:18
沙发
本帖最后由 athenadeveloper 于 2022-10-14 16:52 编辑

请教一下,代码中的处理返回数据段,如附件图。从代码来看是在拆分请求数据,此处是不需要拿到api的返回数据来做处理吗?
image.png80304259.png
回复 使用道具 举报
Ellia.DuanSpreadJS 开发认证
超级版主   /  发表于:2022-10-14 18:32:40
板凳
收到问题,这边调研下给您回复。
回复 使用道具 举报
Ellia.DuanSpreadJS 开发认证
超级版主   /  发表于:2022-10-18 11:27:33
地板
athenadeveloper 发表于 2022-10-14 11:15
请教一下,代码中的处理返回数据段,如附件图。从代码来看是在拆分请求数据,此处是不需要拿到api的返回数 ...

1666063645058.jpg672648620.png
回复 使用道具 举报
清酒℡
注册会员   /  发表于:2023-4-14 18:20:30
5#
想请教一下版主 如果异步函数的调用  这些代码是放在js文件里面的  那这个方法里面的 designer有办法传进去吗  用的技术栈是vue2+v16
回复 使用道具 举报
Lynn.Dou讲师达人认证 悬赏达人认证 SpreadJS 开发认证
超级版主   /  发表于:2023-4-17 18:13:33
6#
文章中designer指的是designer(设计器)对象,您初始化deisgner获取对象后,就可以做后续的处理了。
回复 使用道具 举报
清酒℡
注册会员   /  发表于:2023-6-12 17:20:33
7#
Lynn.Dou 发表于 2023-4-17 18:13
文章中designer指的是designer(设计器)对象,您初始化deisgner获取对象后,就可以做后续的处理了。

再想请教下版主,如果定义了多个异步函数,sheet中也存在多个异步公式,触发计算时,这个函数应该如何改造,并且需要在所有异步公式计算完成后给用户一个计算完成的提示
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 立即注册
返回顶部