惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
O
OpenAI News
量子位
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
小众软件
小众软件
有赞技术团队
有赞技术团队
V
V2EX
宝玉的分享
宝玉的分享
月光博客
月光博客
腾讯CDC
IT之家
IT之家
博客园 - 【当耐特】
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tenable Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Vulnerabilities – Threatpost
美团技术团队
博客园 - 三生石上(FineUI控件)
博客园_首页
博客园 - Franky
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
博客园 - 司徒正美
Project Zero
Project Zero
Cyberwarzone
Cyberwarzone
博客园 - 叶小钗
雷峰网
雷峰网
Latest news
Latest news
S
SegmentFault 最新的问题
罗磊的独立博客
Help Net Security
Help Net Security
Hugging Face - Blog
Hugging Face - Blog
Jina AI
Jina AI
P
Proofpoint News Feed
L
LINUX DO - 热门话题
爱范儿
爱范儿
大猫的无限游戏
大猫的无限游戏
酷 壳 – CoolShell
酷 壳 – CoolShell
K
Kaspersky official blog
A
Arctic Wolf
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
GRAHAM CLULEY
F
Fortinet All Blogs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
PCI Perspectives
PCI Perspectives
Security Archives - TechRepublic
Security Archives - TechRepublic

Anran758's blog

Promise 与异步编程 数据结构实践 MySQL 实践 组件通信: EventBus 的原理解析与应用 Redux 食用指南 计算机网络原理笔记 Accessibility Parsing 无障碍页面分析 软件工程笔记 JavaScript 实现二叉树 React 知识回顾 (优化篇) React 知识回顾 (使用篇) Hexo 常见问题解决方案 React vs Vue webpack + Travis CI 自动部署项目应用 从零构建 webpack 脚手架(基础篇) Flexbox 布局实际用例 Flexbox 布局入门 Hexo blog 的升级与同步方案 将 JSON 数据格式输出至页面上
闭包与链式设计的使用示例
本文作者: anran758 · 2020-12-22 · via Anran758's blog

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

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

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

1
2
3
4
5
6
interface DataType {
code: string;
paymentTransaction: string[];
}

type ListType = DataType[];

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 数据进行尝试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;
}


const list = Array.from(codes.value());
console.log('fetch data before, codes --> ', list);



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);


handleNameMapping(mockItemData);

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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;
}


const list = Array.from(codes.value());
console.log('fetch data before, codes --> ', list);



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);


handleNameMapping(mockItemData);

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