albertgao.common-electron
Version:
个人常用的nodejs/electron模块
262 lines (255 loc) • 7.51 kB
JavaScript
const CP = require('child_process')
const Path = require('path')
const { nanoid } = require('nanoid')
const _ = require('lodash')
const vue = require('vue')
/**
*
* @returns {number}在nodejs或者js中返回Cpu数量
*/
function cpus() {
try {
return Math.max(1, require('os').cpus().length)
} catch (error) {
return Math.max(1, navigator.hardwareConcurrency)
}
}
/**
* 用子进程写入csv
*
* @param {string}path 文件路径
* @param {object[]}rows (可选)首次导入的数据
* @returns {object} .worker worker, setTitles(titles: string[]) 标题
*/
function csvAcc(path, rows) {
const worker = CP.fork(Path.join(__dirname, './csv-Accumulator.js'))
worker.setMaxListeners(Infinity)
worker.send({ path })
const work = {
worker,
setTitles(titles) {
worker.send({ titles })
},
push(...lines) {
return new Promise((resolve) => {
const id = nanoid()
worker.on(
'message',
({ complete, id: idCheck }) => complete && idCheck === id && resolve()
)
worker.send({ lines, id })
})
},
terminate: () => worker.kill(2),
kill: () => worker.kill(2)
}
rows && work.push(...rows)
return work
}
/**
* @param {string}path 路径
* @param {*}obj (可选)可以转为json的内容
* @returns {object} {worker,kill,terminate,write}
*/
function jsonWriter(path, obj) {
const Path = require('path')
const worker = CP.fork(Path.join(__dirname, './json-Writer.js'))
worker.setMaxListeners(Infinity)
worker.send({ path })
const work = {
worker: worker,
kill: () => worker.kill(2),
terminate: () => worker.kill(2),
write: (...args) =>
new Promise((resolve) => {
const id = nanoid()
worker.on(
'message',
({ complete, id: id1 }) => id1 === id && complete && resolve()
)
let postMsg = {
path: _.find(args, (e) =>
/string/i.test(Object.prototype.toString.call(e))
),
json: _.find(
args,
(e) => !/string/i.test(Object.prototype.toString.call(e))
),
id
}
worker.send(postMsg)
postMsg = null // 释放内存
})
}
obj && work.write(obj)
work.output = work.write
return work
}
/**
* @param {string}path 路径
* @param {object[] | Array[String[]]}data *
* @returns {object} {worker,kill,terminate,write}
*/
function xlsxWriter(path, data) {
const Path = require('path')
const worker = CP.fork(Path.join(__dirname, 'xlsx-Writer.js'))
worker.setMaxListeners(Infinity)
worker.send({ path })
const work = {
worker: worker,
kill: () => worker.kill(2),
terminate: () => worker.kill(2),
write: (...args) =>
new Promise((resolve) => {
const id = nanoid()
worker.on(
'message',
({ complete, id: id1 }) => id1 === id && complete && resolve()
)
let postMsg = {
path: _.find(args, (e) =>
/string/i.test(Object.prototype.toString.call(e))
),
json: _.find(
args,
(e) => !/string/i.test(Object.prototype.toString.call(e))
),
id
}
worker.send(postMsg)
postMsg = null // 释放内存
})
}
data && work.write(data)
work.output = work.write
return work
}
/**
* 创建一个json写入线程
*
* @param {string}path 要导出的json文件路径
* @param {*[] | object}rows 首次要导入的数据
* @returns {object}返回{worker,kill,terminate,assign}
*/
function jsonAcc(path, rows) {
const { nanoid } = require('nanoid')
const cp = require('child_process')
const Path = require('path')
const worker = cp.fork(Path.join(__dirname, './json-Accumulator.js'))
worker.setMaxListeners(Infinity)
path && worker.send({ path })
const work = {
worker: worker,
kill: () => worker.kill(2),
terminate: () => worker.kill(2),
assign: (...args) =>
new Promise((resolve) => {
const id = nanoid()
worker.on('message', ({ id: id1 }) => id1 === id && resolve())
// 向worker推送rows
// let postMsg = { path, rows, id }
let postMsg = {
path: _.find(args, (e) =>
/string/i.test(Object.prototype.toString.call(e))
),
rows: _.find(
args,
(e) => !/string/i.test(Object.prototype.toString.call(e))
),
id
}
worker.send(postMsg)
postMsg = null // 释放内存
})
}
rows && work.assign(rows)
work.push = work.append = work.assign
return work
}
/**
* 对任务进行最优化队列
*
* @param {object[]}tasks 任务队列
* @param {Function}tasks.func 该任务所用函数,返回值必须是Promise
* @param {*[]}tasks.args 该任务所用函数的参数
* @param {string | null}tasks.key 该任务的返回值如果是Object, 其中有一个key是Promise的话,key的名称
* @param {Function}tasks.then 回调该任务返回值[key]的promise
* @param {Function}[watcher=console.log] 回调{waiting, finished, processing, total}
* @param {number}[maxChunk=cpus()] 队列最大长度,默认为cpu数量
* @returns {Promise<*[]>} 返回运行结果数组
*/
function queque(tasks = [], watcher = console.log, maxChunk = cpus()) {
return new Promise((resolve) => {
const beforeProcessing = vue.reactive(
// 未开始的任务
[...tasks].map((obj, index) => {
obj.id = nanoid()
obj.index = index
return obj
})
)
const total = tasks.length
const processing = vue.reactive([]) // 执行中的任务
const results = vue.reactive([])
const finished = vue.computed(
() => total - processing.length - beforeProcessing.length
)
const progress = vue.computed(
() => `${~~((finished.value * 1000) / total) / 10}%`
)
const detail = vue.computed(
() => `${finished.value}/${total} (${progress})`
)
vue.watchEffect(() => {
watcher &&
watcher({
waiting: beforeProcessing.length,
finished: finished.value,
processing: processing.length,
progress,
detail,
total
})
})
vue.watchEffect(
() =>
processing.length + beforeProcessing.length === 0 && resolve(results)
)
vue.watchEffect(() => {
while (processing.length < maxChunk && beforeProcessing.length > 0) {
let {
func = async () => 0,
args = [],
key = null,
then = () => 0,
index
} = beforeProcessing.shift() // 提取出waiting的一个元素
const id = nanoid()
const job = vue.reactive({
id,
promise: key ? func(...args)[key] : func(...args)
})
processing.push(job) // 把waiting的最后一个元素充进去
// eslint-disable-next-line promise/catch-or-return
job.promise.then((result) => {
// 如果该promise已经fulfill
_.pullAt(
// 删除该元素
processing,
_.findIndex(processing, ({ id: idCheck }) => id === idCheck) // 找到一个符合该id的job,然后删除
)
results[index] = result
return then(result) // 回调结果
})
args = null // args可能比较大,所以清空内存
}
})
})
}
module.exports = {
csvAcc,
queque,
jsonAcc,
jsonWriter,
xlsxWriter
}