闭包与链式设计的使用示例

视觉狂
• 阅读 1405

最近遇到了个按需请求数据的需求,非常适合用于讲解闭包与链式设计的例子,故来分享一下思路。

大致需求如下: 目前有个 list, list 中每项 item 都是可展开的折叠项。当展开某个折叠项时,需要根据 item 的 code 另外去取 name 的映射。考虑到列表的数据量非常大,且一次性查询过多 code 时,接口的查询效率会明显降低,故采用按需请求映射的方案。

屏蔽与本例无关的属性,瘦身后的 list 数据结构大致如下:

interface DataType {
  code: string;
  paymentTransaction: string[];
}

type ListType = DataType[];

我们知道大型企业中的数据会比较复杂,比较常见的一种情况是数据中有一个 id 或 code 是用于跟另一个数据项相关联的。学习过数据库的同学很容易就联想到了外键这个概念。

现在我们就要取出这些 code 发送给服务端去查询。考虑到 code 可能会有重复,因此可以将 codes 存入 Set 中,利用 Set 的特性去重。除此之外,为了使 name 映射可以被复用,每次从接口返回的 name 映射将会被缓存起来。若下次再触发事件时有对应的 key,便不再查询。

我们可以将这段逻辑抽离出来作为一个依赖收集的函数:

const mapping = new Map();

function collectionCodes(initCodes?: string[] | Set<string>) {
  const codes = new Set<string>(initCodes)

  return {
    append(code: string) {
      if (!mapping.has(code)) {
        codes.add(code);
      }

      return this;
    },
    empty() {
      return !codes.size;
    },
    value() {
      return codes;
    },
  }
}

collectionCodes 函数是用于收集 codes。它内部利用了闭包的特性将 codes 缓存了起来,并且在添加新的 code 之前会判断 code 在 local 的映射中是否已经存在。append 返回的 this 是经典的链式调用设计,允许多次链式添加。当本次依赖收集结束后,调用 value 方法获取最终的 codes。

可以写一些简单的 mock 数据进行尝试:

function handleNameMapping(data: DataType) {
  const codes = collectionCodes()
    .append(data.code)
    .append('code-append-1')
    .append('code-append-1')
    .append('code-append-2');

  data.paymentTransaction.forEach(code => codes.append(code));

  if (codes.empty()) {
    console.log('can get values from existing mapping.')
    return;
  }

  // 如果请求的数据需要转为数组,可以 Array.from 进行转换
  const list = Array.from(codes.value());
  console.log('fetch data before, codes --> ', list);

  // mock 获取数据后拿到 name mapping 后,存入 mapping 中的行为.
  // 注意,Set 类型也可以用 forEach 方法,不一定得转为数组才可以操作
  list.forEach(code => mapping.set(code, `random-name-${Math.random()}`))
}

const mockItemData = {
  code: 'code-main',
  paymentTransaction: [
    'code-payment-4',
    'code-payment-1',
    'code-payment-2',
    'code-payment-1',
    'code-payment-3',
  ]
}

handleNameMapping(mockItemData);
// fetch data before, codes -->  (7) ["code-main", "code-append-1", "code-append-2", "code-payment-4", "code-payment-1", "code-payment-2", "code-payment-3"]

handleNameMapping(mockItemData);
// can get values from existing mapping.

handleNameMapping 在发起请求前会做 code 收集,若本次收集中没有需要 fetch 的 code,那就避免发送无用的 HTTP 请求,从而达到了优化的目的。

最终示例的 TS 代码如下。若想直接在控制台尝试效果的话,可以通过 ts 官网中的 Playground 编译为可直接运行的 js 代码:

interface DataType {
  code: string;
  paymentTransaction: string[];
}

const mapping = new Map();

function collectionCodes(initCodes?: string[] | Set<string>) {
  const codes = new Set<string>(initCodes);

  return {
    append(code: string) {
      if (!mapping.has(code)) {
        codes.add(code);
      }

      return this;
    },
    empty() {
      return !codes.size;
    },
    value() {
      return codes;
    },
  };
}

function handleNameMapping(data: DataType) {
  const codes = collectionCodes()
    .append(data.code)
    .append('code-append-1')
    .append('code-append-1')
    .append('code-append-2');

  data.paymentTransaction.forEach((code) => codes.append(code));

  if (codes.empty()) {
    console.log('can get values from existing mapping.');
    return;
  }

  // 如果请求的数据需要转为数组,可以 Array.from 进行转换
  const list = Array.from(codes.value());
  console.log('fetch data before, codes --> ', list);

  // mock 获取数据后拿到 name mapping 后,存入 mapping 中的行为.
  // 注意,Set 类型也可以用 forEach 方法,不一定得转为数组才可以操作
  list.forEach(code => mapping.set(code, `random-name-${Math.random()}`))
}

const mockItemData = {
  code: 'code-main',
  paymentTransaction: [
    'code-payment-4',
    'code-payment-1',
    'code-payment-2',
    'code-payment-1',
    'code-payment-3',
  ],
};

handleNameMapping(mockItemData);
// fetch data before, codes -->  (7) ["code-main", "code-append-1", "code-append-2", "code-payment-4", "code-payment-1", "code-payment-2", "code-payment-3"]

handleNameMapping(mockItemData);
// can get values from existing mapping.

本例的分析就到此结束了,虽然在本例中链式调用没有充分展示出自己的优势,但也可以作为一个设计思路用于参考。


原文出自: 闭包与链式设计的使用示例 | Anran758's blog

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
4年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Stella981 Stella981
4年前
List的Select 和Select().tolist()
List<PersondelpnewList<Person{newPerson{Id1,Name"小明1",Age11,Sign0},newPerson{Id2,Name"小明2",Age12,
Easter79 Easter79
4年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
4年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
4年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Vitess全局唯一ID生成的实现方案 | 京东云技术团队
为了标识一段数据,通常我们会为其指定一个唯一id,比如利用MySQL数据库中的自增主键。但是当数据量非常大时,仅靠数据库的自增主键是远远不够的,并且对于分布式数据库只依赖MySQL的自增id无法满足全局唯一的需求。因此,产生了多种解决方案,如UUID,Sn
视觉狂
视觉狂
Lv1
林间新绿一重重,小蕾深藏数点红。
文章
4
粉丝
0
获赞
0