mock-service-cli 2.3.2 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +12 -1
- package/lib/asyncTaskQueue.js +74 -0
- package/lib/manageMockFiles.js +24 -14
- package/lib/mockServer.js +23 -6
- package/lib/utils.js +128 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.4.3](https://github.com/chandq/mock-service-cli/compare/v2.4.2...v2.4.3) (2022-01-20)
|
|
6
|
+
|
|
7
|
+
### [2.4.2](https://github.com/chandq/mock-service-cli/compare/v2.4.1...v2.4.2) (2022-01-07)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* 缓存mock统计信息 ([1ee87cd](https://github.com/chandq/mock-service-cli/commit/1ee87cd041e68b9f17b64e9e3cb906681cb855fc))
|
|
13
|
+
|
|
14
|
+
### [2.4.1](https://github.com/chandq/mock-service-cli/compare/v2.4.0...v2.4.1) (2022-01-06)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* 修复无法同时保留相同url不同请求类型的响应数据 ([90326dc](https://github.com/chandq/mock-service-cli/commit/90326dc556760eb81efadd76b031366452c95d2b))
|
|
20
|
+
|
|
21
|
+
## [2.4.0](https://github.com/chandq/mock-service-cli/compare/v2.3.2...v2.4.0) (2022-01-05)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* 增加异步任务队列,移出不必要模块 ([c7f3608](https://github.com/chandq/mock-service-cli/commit/c7f3608e4d0185434154d6f8d84889f281147022))
|
|
27
|
+
|
|
5
28
|
### [2.3.2](https://github.com/chandq/mock-service-cli/compare/v2.3.1...v2.3.2) (2022-01-02)
|
|
6
29
|
|
|
7
30
|
|
package/README.md
CHANGED
|
@@ -119,7 +119,18 @@ const mockjs = require('mockjs');
|
|
|
119
119
|
module.exports = {
|
|
120
120
|
// 使用 mockjs 等三方库
|
|
121
121
|
'GET /api/tags': mockjs.mock({
|
|
122
|
-
'list|100': [
|
|
122
|
+
'list|100': [
|
|
123
|
+
{
|
|
124
|
+
'NO|+1': 1,
|
|
125
|
+
city: '@city',
|
|
126
|
+
maintainType: '@cname(3, 5)',
|
|
127
|
+
urgentType: '@cword(3, 5)',
|
|
128
|
+
'isCrash|1': 'true',
|
|
129
|
+
createTime: '@date',
|
|
130
|
+
'value|1-100': 50,
|
|
131
|
+
'type|0-2': 1
|
|
132
|
+
}
|
|
133
|
+
]
|
|
123
134
|
})
|
|
124
135
|
};
|
|
125
136
|
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @description: 异步任务队列
|
|
3
|
+
* @Date: 2022-01-04 10:46:20
|
|
4
|
+
* @LastEditors: chendq
|
|
5
|
+
* @LastEditTime: 2022-01-06 13:17:21
|
|
6
|
+
* @Author: chendq
|
|
7
|
+
*/
|
|
8
|
+
// const { getDataType } = require('./utils');
|
|
9
|
+
class AsyncTaskQueue {
|
|
10
|
+
constructor(isSaveItem = false) {
|
|
11
|
+
this.list = [];
|
|
12
|
+
this.index = 0;
|
|
13
|
+
this.isStop = false;
|
|
14
|
+
this.isParallel = false;
|
|
15
|
+
this.isSaveItem = isSaveItem; // 执行完成后的数据是否保留
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
next() {
|
|
19
|
+
// 加限制
|
|
20
|
+
if (this.index >= this.list.length - 1 || this.isStop) return;
|
|
21
|
+
if (!this.isSaveItem) {
|
|
22
|
+
this.list.splice(this.index, 1, '');
|
|
23
|
+
}
|
|
24
|
+
const cur = this.list[++this.index];
|
|
25
|
+
cur(this.next.bind(this));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @description: 增加异步任务
|
|
29
|
+
* @param {array} fn
|
|
30
|
+
*/
|
|
31
|
+
add(...fn) {
|
|
32
|
+
this.list.push(...fn);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @description: 按序执行异步任务
|
|
36
|
+
*/
|
|
37
|
+
run() {
|
|
38
|
+
const cur = this.list[this.index];
|
|
39
|
+
typeof cur === 'function' && cur(this.next.bind(this));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @description: 并发执行异步任务
|
|
44
|
+
*/
|
|
45
|
+
parallelRun() {
|
|
46
|
+
this.isParallel = true;
|
|
47
|
+
for (const fn of this.list) {
|
|
48
|
+
fn(this.next.bind(this));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @description: 暂停执行异步任务
|
|
54
|
+
*/
|
|
55
|
+
stop() {
|
|
56
|
+
this.isStop = true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @description: 重试执行异步任务
|
|
61
|
+
*/
|
|
62
|
+
retry() {
|
|
63
|
+
this.isStop = false;
|
|
64
|
+
run();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @description: 继续执行下一个异步任务
|
|
68
|
+
*/
|
|
69
|
+
goOn() {
|
|
70
|
+
this.isStop = false;
|
|
71
|
+
this.next();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
module.exports = AsyncTaskQueue;
|
package/lib/manageMockFiles.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @description: 生成Mock文件、获取mock数据统计
|
|
3
3
|
* @Date: 2021-12-22 16:57:08
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime: 2022-01-
|
|
5
|
+
* @LastEditTime: 2022-01-06 13:41:12
|
|
6
6
|
* @Author: chendq
|
|
7
7
|
*/
|
|
8
8
|
const fsa = require('fs-extra'),
|
|
@@ -71,22 +71,23 @@ const genMockFiles = async function ({ url: apiUrl, method, data: resJsonData, d
|
|
|
71
71
|
const normalizeApiUrl = filePath2ApiUrl(path.normalize(apiUrl));
|
|
72
72
|
const mockListFilePath = path.resolve(process.cwd(), `${mockDir}/mock-list.json`);
|
|
73
73
|
const mockFilePath = path.resolve(process.cwd(), `${mockDir}/${encodeURIComponent(path.normalize(apiUrl))}.json`);
|
|
74
|
-
let newMockListContent = {}
|
|
75
|
-
oldMockListContent = null;
|
|
74
|
+
let newMockListContent = {};
|
|
76
75
|
if (fsa.pathExistsSync(mockListFilePath)) {
|
|
77
|
-
|
|
78
|
-
newMockListContent = _.cloneDeep(oldMockListContent);
|
|
76
|
+
newMockListContent = getFileLatestContent(mockListFilePath);
|
|
79
77
|
} else {
|
|
80
78
|
fsa.ensureFileSync(mockListFilePath);
|
|
81
79
|
}
|
|
82
80
|
|
|
81
|
+
let isChange = false;
|
|
83
82
|
// update mock-list.json file
|
|
84
83
|
if (!newMockListContent.hasOwnProperty(normalizeApiUrl)) {
|
|
85
84
|
newMockListContent[normalizeApiUrl] = [method];
|
|
85
|
+
isChange = true;
|
|
86
86
|
} else if (!newMockListContent[normalizeApiUrl].includes(method)) {
|
|
87
87
|
newMockListContent[normalizeApiUrl].push(method);
|
|
88
|
+
isChange = true;
|
|
88
89
|
}
|
|
89
|
-
if (
|
|
90
|
+
if (isChange) {
|
|
90
91
|
Object.keys(newMockListContent).length > 0 &&
|
|
91
92
|
fs.writeFileSync(mockListFilePath, JSON.stringify(newMockListContent, null, 2));
|
|
92
93
|
}
|
|
@@ -103,13 +104,19 @@ const genMockFiles = async function ({ url: apiUrl, method, data: resJsonData, d
|
|
|
103
104
|
return;
|
|
104
105
|
}
|
|
105
106
|
mockFileContent[method] = resJsonData;
|
|
106
|
-
|
|
107
|
+
try {
|
|
108
|
+
await writeStream(mockFilePath, JSON.stringify(mockFileContent, null, 2));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
LOGGER.error(`Update file: ${method} ${apiUrl} ${mockFilePath}`);
|
|
111
|
+
}
|
|
107
112
|
LOGGER.log(`Update file: ${method} ${apiUrl} ${mockFilePath}`);
|
|
108
113
|
} else {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
try {
|
|
115
|
+
await writeStream(mockFilePath, JSON.stringify({ [method]: resJsonData }, null, 2));
|
|
116
|
+
} catch (error) {
|
|
117
|
+
LOGGER.error(`New file: ${method} ${apiUrl} ${mockFilePath}`);
|
|
118
|
+
}
|
|
119
|
+
LOGGER.log(`New file: ${method} ${apiUrl} ${mockFilePath}`);
|
|
113
120
|
}
|
|
114
121
|
// console.debug('file::', `${mockDir}/${apiUrl}`, newMockListContent);
|
|
115
122
|
};
|
|
@@ -180,7 +187,8 @@ const deepCollectMockData = function (mockDataMap, specialDir = '../mock') {
|
|
|
180
187
|
*/
|
|
181
188
|
const getMockStatFromDir = dirPath => {
|
|
182
189
|
if (!fsa.pathExistsSync(dirPath)) {
|
|
183
|
-
|
|
190
|
+
getLogger(path.resolve(process.cwd())).error(`入参无效,${dirPath} 目录不存在`);
|
|
191
|
+
return;
|
|
184
192
|
}
|
|
185
193
|
const mockDataMap = {};
|
|
186
194
|
deepCollectMockData(mockDataMap, dirPath);
|
|
@@ -214,7 +222,8 @@ const getMockStatFromDir = dirPath => {
|
|
|
214
222
|
*/
|
|
215
223
|
const getMockStatFromFile = filePath => {
|
|
216
224
|
if (!fsa.pathExistsSync(filePath)) {
|
|
217
|
-
|
|
225
|
+
getLogger(path.resolve(process.cwd())).error(`入参无效,${filePath} 文件不存在`);
|
|
226
|
+
return;
|
|
218
227
|
}
|
|
219
228
|
const mockDataMap = {};
|
|
220
229
|
collectMockDataFromJsFile(mockDataMap, filePath);
|
|
@@ -229,7 +238,8 @@ const getMockStatFromFile = filePath => {
|
|
|
229
238
|
*/
|
|
230
239
|
const hasMockApi = function (mockDataMap, apiUrl, method) {
|
|
231
240
|
if (!apiUrl || !method || !mockDataMap || !(mockDataMap instanceof Object)) {
|
|
232
|
-
|
|
241
|
+
getLogger(path.resolve(process.cwd())).error(`入参无效,${mockDataMap} 必须是 mock数据的Object对象`);
|
|
242
|
+
return;
|
|
233
243
|
}
|
|
234
244
|
return mockDataMap.hasOwnProperty(`${method.toLocaleLowerCase()} ${path.normalize(apiUrl)}`);
|
|
235
245
|
};
|
package/lib/mockServer.js
CHANGED
|
@@ -7,7 +7,7 @@ const express = require('express'), // 引入express
|
|
|
7
7
|
chalk = require('chalk');
|
|
8
8
|
const ifaces = os.networkInterfaces();
|
|
9
9
|
const { Server } = require('socket.io');
|
|
10
|
-
const { dateFormat, logger, SupportMethods, getFileLatestContent, filePath2ApiUrl } = require('./utils');
|
|
10
|
+
const { dateFormat, logger, SupportMethods, getFileLatestContent, filePath2ApiUrl, isEmptyObj } = require('./utils');
|
|
11
11
|
const { genMockFiles, getMockStatFromDir, getMockStatFromFile } = require('./manageMockFiles');
|
|
12
12
|
|
|
13
13
|
const methodRegExp = new RegExp(`(${SupportMethods.join('|')}) +(.*)`, 'i');
|
|
@@ -16,9 +16,13 @@ const app = express();
|
|
|
16
16
|
const log = logger(process.env.SILENT);
|
|
17
17
|
const argv = JSON.parse(process.env.ARGV);
|
|
18
18
|
let socketServer = null; // Socket server instance
|
|
19
|
+
const AsyncTaskQueue = require('./asyncTaskQueue');
|
|
20
|
+
const saveDataAsyncTask = new AsyncTaskQueue();
|
|
19
21
|
|
|
20
22
|
let count = 0,
|
|
21
|
-
fileCount = 0
|
|
23
|
+
fileCount = 0, // 记录mock api个数,mock file个数
|
|
24
|
+
mockDirStat = {}, // 缓存mock目录的统计信息
|
|
25
|
+
mockFileStat = {}; // 缓存mock文件的统计信息
|
|
22
26
|
let done = false;
|
|
23
27
|
if (!process.env.PORT) {
|
|
24
28
|
portfinder.basePort = 8090;
|
|
@@ -145,14 +149,27 @@ if (process.env.SOCKET_SERVER) {
|
|
|
145
149
|
colors.green(`Socket client ${clientIp} has connected. --- ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
146
150
|
);
|
|
147
151
|
socket.on('mock-dir-stat', function (dir) {
|
|
148
|
-
socket.emit('mock-dir-stat', getMockStatFromDir(dir));
|
|
152
|
+
socket.emit('mock-dir-stat', isEmptyObj(mockDirStat) ? mockDirStat = getMockStatFromDir(dir) : mockDirStat);
|
|
149
153
|
});
|
|
150
154
|
socket.on('mock-file-stat', function (filePath) {
|
|
151
|
-
socket.emit('mock-file-stat', getMockStatFromFile(filePath));
|
|
155
|
+
socket.emit('mock-file-stat', isEmptyObj(mockFileStat) ? mockFileStat = getMockStatFromFile(filePath) : mockFileStat);
|
|
152
156
|
});
|
|
153
|
-
|
|
157
|
+
const async = args => {
|
|
158
|
+
return async next => {
|
|
159
|
+
await genMockFiles(args);
|
|
160
|
+
next();
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
socket.on('save-data', function (data) {
|
|
154
164
|
// console.log('lis save-data:', data);
|
|
155
|
-
|
|
165
|
+
saveDataAsyncTask.add(async(data));
|
|
166
|
+
|
|
167
|
+
if (saveDataAsyncTask.list.length === 1) {
|
|
168
|
+
saveDataAsyncTask.run();
|
|
169
|
+
} else {
|
|
170
|
+
saveDataAsyncTask.next();
|
|
171
|
+
}
|
|
172
|
+
// await genMockFiles(data);
|
|
156
173
|
});
|
|
157
174
|
|
|
158
175
|
socket.on('disconnect', function () {
|
package/lib/utils.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* @description: 工具函数库
|
|
3
3
|
* @Date: 2021-12-25 17:52:48
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime: 2022-01-
|
|
5
|
+
* @LastEditTime: 2022-01-05 20:21:07
|
|
6
6
|
* @Author: chendq
|
|
7
7
|
*/
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
// const JSONStream = require('JSONStream');
|
|
10
11
|
/**
|
|
11
12
|
* @description: 输出和错误输出写入不同文件
|
|
12
13
|
* @param {string} logFileDirPath
|
|
@@ -18,11 +19,11 @@ const getLogger = function (logFileDirPath) {
|
|
|
18
19
|
encoding: 'utf8' // utf8编码
|
|
19
20
|
};
|
|
20
21
|
const output = fs.createWriteStream(
|
|
21
|
-
path.resolve(logFileDirPath ? logFileDirPath : process.cwd(), './stdout.log'),
|
|
22
|
+
path.resolve(logFileDirPath ? logFileDirPath : path.resolve(process.cwd()), './stdout.log'),
|
|
22
23
|
options
|
|
23
24
|
);
|
|
24
25
|
const errorOutput = fs.createWriteStream(
|
|
25
|
-
path.resolve(logFileDirPath ? logFileDirPath : process.cwd(), './stderr.log'),
|
|
26
|
+
path.resolve(logFileDirPath ? logFileDirPath : path.resolve(process.cwd()), './stderr.log'),
|
|
26
27
|
options
|
|
27
28
|
);
|
|
28
29
|
// 自定义日志打印
|
|
@@ -38,9 +39,9 @@ const getLogger = function (logFileDirPath) {
|
|
|
38
39
|
};
|
|
39
40
|
/**
|
|
40
41
|
* @description: 格式化时间日期
|
|
41
|
-
* @param {
|
|
42
|
-
* @param {
|
|
43
|
-
* @returns {
|
|
42
|
+
* @param {string} fmt 时间日期字符串格式化模板
|
|
43
|
+
* @param {date} date 时间日期Date
|
|
44
|
+
* @returns {string}
|
|
44
45
|
*/
|
|
45
46
|
function dateFormat(fmt, date = new Date()) {
|
|
46
47
|
let ret;
|
|
@@ -65,8 +66,8 @@ function dateFormat(fmt, date = new Date()) {
|
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
67
68
|
* @description: 控制是否写日志
|
|
68
|
-
* @param {
|
|
69
|
-
* @return {
|
|
69
|
+
* @param {boolean} isSilent 是否静默
|
|
70
|
+
* @return {object}
|
|
70
71
|
*/
|
|
71
72
|
function logger(isSilent = false) {
|
|
72
73
|
let logObj = null;
|
|
@@ -96,8 +97,12 @@ const isValidMethod = function (m) {
|
|
|
96
97
|
* @return {object} 文件内容
|
|
97
98
|
*/
|
|
98
99
|
const getFileLatestContent = function (filePath) {
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
try {
|
|
101
|
+
delete require.cache[require.resolve(filePath)];
|
|
102
|
+
return require(path.resolve(process.cwd(), filePath));
|
|
103
|
+
} catch (error) {
|
|
104
|
+
getLogger(path.resolve(process.cwd())).error(filePath, require.cache[require.resolve(filePath)], error);
|
|
105
|
+
}
|
|
101
106
|
};
|
|
102
107
|
/**
|
|
103
108
|
* @description: 文件路径转成API的url
|
|
@@ -115,6 +120,14 @@ const filePath2ApiUrl = function (filePath) {
|
|
|
115
120
|
const getDataType = function (data) {
|
|
116
121
|
return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
|
|
117
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* @description: 判断对象是否为空
|
|
125
|
+
* @param {object} obj
|
|
126
|
+
* @return {boolean}
|
|
127
|
+
*/
|
|
128
|
+
const isEmptyObj = function (obj) {
|
|
129
|
+
return getDataType(obj) === 'object' && Object.keys(obj).length === 0;
|
|
130
|
+
};
|
|
118
131
|
/**
|
|
119
132
|
* @description: 流方式写文件(适合写超大文件)
|
|
120
133
|
* @param {string} filePath
|
|
@@ -123,20 +136,22 @@ const getDataType = function (data) {
|
|
|
123
136
|
*/
|
|
124
137
|
const writeStream = function (filePath, data) {
|
|
125
138
|
// 创建一个可写流 也会创建一个test1.txt文件
|
|
126
|
-
const writerStream = fs.createWriteStream(filePath);
|
|
127
139
|
return new Promise((resolve, reject) => {
|
|
140
|
+
const writerStream = fs.createWriteStream(filePath);
|
|
128
141
|
// 将数据写入流
|
|
129
142
|
writerStream.write(data, 'utf-8');
|
|
130
143
|
// 标记文件的结束
|
|
131
144
|
writerStream.end();
|
|
132
|
-
writerStream.on('finish', () => {
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
writerStream.on('finish', () => {});
|
|
146
|
+
writerStream.on('close', () => {
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
resolve(data);
|
|
149
|
+
});
|
|
135
150
|
});
|
|
136
151
|
|
|
137
152
|
writerStream.on('error', err => {
|
|
153
|
+
getLogger(path.resolve(process.cwd())).error(filePath, require.cache[require.resolve(filePath)], err);
|
|
138
154
|
reject(err);
|
|
139
|
-
console.error(`${filePath} 写入失败`);
|
|
140
155
|
});
|
|
141
156
|
});
|
|
142
157
|
};
|
|
@@ -145,7 +160,7 @@ const writeStream = function (filePath, data) {
|
|
|
145
160
|
* @param {string} filePath
|
|
146
161
|
* @return {*}
|
|
147
162
|
*/
|
|
148
|
-
const
|
|
163
|
+
const readStream = function (filePath) {
|
|
149
164
|
let data = '';
|
|
150
165
|
// 创建可读流
|
|
151
166
|
return new Promise((resovle, reject) => {
|
|
@@ -166,6 +181,99 @@ const readeStream = function (filePath) {
|
|
|
166
181
|
});
|
|
167
182
|
};
|
|
168
183
|
|
|
184
|
+
/**
|
|
185
|
+
* 防抖函数
|
|
186
|
+
* 当函数被连续调用时,该函数并不执行,只有当其全部停止调用超过一定时间后才执行1次。
|
|
187
|
+
* 例如:上电梯的时候,大家陆陆续续进来,电梯的门不会关上,只有当一段时间都没有人上来,电梯才会关门。
|
|
188
|
+
* @param {F} func
|
|
189
|
+
* @param {number} wait
|
|
190
|
+
* @returns {DebounceFunc<F>}
|
|
191
|
+
*/
|
|
192
|
+
const debounce = (func, wait) => {
|
|
193
|
+
let timeout;
|
|
194
|
+
let canceled = false;
|
|
195
|
+
const f = function (...args) {
|
|
196
|
+
if (canceled) return;
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
timeout = setTimeout(() => {
|
|
199
|
+
func.call(this, ...args);
|
|
200
|
+
}, wait);
|
|
201
|
+
};
|
|
202
|
+
f.cancel = () => {
|
|
203
|
+
clearTimeout(timeout);
|
|
204
|
+
canceled = true;
|
|
205
|
+
};
|
|
206
|
+
return f;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* 节流函数
|
|
210
|
+
* 节流就是节约流量,将连续触发的事件稀释成预设评率。 比如每间隔1秒执行一次函数,无论这期间触发多少次事件。
|
|
211
|
+
* 这有点像公交车,无论在站点等车的人多不多,公交车只会按时来一班,不会来一个人就来一辆公交车。
|
|
212
|
+
* @param {F} func
|
|
213
|
+
* @param {number} wait
|
|
214
|
+
* @param {boolean} immediate
|
|
215
|
+
* @returns {ThrottleFunc<F>}
|
|
216
|
+
*/
|
|
217
|
+
const throttle = (func, wait, immediate) => {
|
|
218
|
+
let timeout;
|
|
219
|
+
let canceled = false;
|
|
220
|
+
let lastCalledTime = 0;
|
|
221
|
+
const f = function (...args) {
|
|
222
|
+
if (canceled) return;
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
const call = () => {
|
|
225
|
+
lastCalledTime = now;
|
|
226
|
+
func.call(this, ...args);
|
|
227
|
+
};
|
|
228
|
+
// 第一次执行
|
|
229
|
+
if (lastCalledTime === 0) {
|
|
230
|
+
if (immediate) {
|
|
231
|
+
return call();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
lastCalledTime = now;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const remain = lastCalledTime + wait - now;
|
|
238
|
+
if (remain > 0) {
|
|
239
|
+
clearTimeout(timeout);
|
|
240
|
+
timeout = setTimeout(() => call(), wait);
|
|
241
|
+
} else {
|
|
242
|
+
call();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
f.cancel = () => {
|
|
246
|
+
clearTimeout(timeout);
|
|
247
|
+
canceled = true;
|
|
248
|
+
};
|
|
249
|
+
return f;
|
|
250
|
+
};
|
|
251
|
+
// /**
|
|
252
|
+
// * @description: 带模糊搜索的分块读取大JSON文件
|
|
253
|
+
// * @param {string} filePath
|
|
254
|
+
// * @return {*}
|
|
255
|
+
// */
|
|
256
|
+
// const readBigJson = function (filePath) {
|
|
257
|
+
// return new Promise((resolve, reject) => {
|
|
258
|
+
// let res = '';
|
|
259
|
+
// const readable = fs.createReadStream(filePath, {
|
|
260
|
+
// encoding: 'utf8',
|
|
261
|
+
// highWaterMark: 10
|
|
262
|
+
// });
|
|
263
|
+
// const parser = JSONStream.parse('*');
|
|
264
|
+
// readable.pipe(parser);
|
|
265
|
+
// parser.on('end', function () {
|
|
266
|
+
// // I know it ends here,
|
|
267
|
+
// console.log('end::', res);
|
|
268
|
+
// resolve(res);
|
|
269
|
+
// });
|
|
270
|
+
// parser.on('data', function (data) {
|
|
271
|
+
// res += data;
|
|
272
|
+
// console.log('yy::', data);
|
|
273
|
+
// });
|
|
274
|
+
// });
|
|
275
|
+
// };
|
|
276
|
+
|
|
169
277
|
module.exports = {
|
|
170
278
|
getLogger,
|
|
171
279
|
dateFormat,
|
|
@@ -175,6 +283,9 @@ module.exports = {
|
|
|
175
283
|
getFileLatestContent,
|
|
176
284
|
filePath2ApiUrl,
|
|
177
285
|
getDataType,
|
|
286
|
+
isEmptyObj,
|
|
178
287
|
writeStream,
|
|
179
|
-
|
|
288
|
+
readStream,
|
|
289
|
+
debounce,
|
|
290
|
+
throttle
|
|
180
291
|
};
|