w-data-syncer
Version:
A tool for data detection and synchronization.
399 lines (325 loc) • 11.2 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WDataSyncer.mjs - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<script src="scripts/nav.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav >
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDataSourceFromCsv">WDataSourceFromCsv</a></li><li><a href="global.html#WDataSyncer">WDataSyncer</a></li></ul>
</nav>
<div id="main">
<h1 class="page-title">WDataSyncer.mjs</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import map from 'lodash-es/map.js'
import size from 'lodash-es/size.js'
import sortBy from 'lodash-es/sortBy.js'
import evem from 'wsemi/src/evem.mjs'
import genPm from 'wsemi/src/genPm.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import ispint from 'wsemi/src/ispint.mjs'
import isearr from 'wsemi/src/isearr.mjs'
import isbol from 'wsemi/src/isbol.mjs'
import cint from 'wsemi/src/cint.mjs'
import haskey from 'wsemi/src/haskey.mjs'
import ltdtDiffByKey from 'wsemi/src/ltdtDiffByKey.mjs'
/**
* 資料單向同步程序
* @param {Object} src - 資料來源,需提供 select 函數 (async)
* @param {Object} tar - 資料目標,需提供 select、insert、save、del 函數 (async)
* @param {Object} opt - 設定物件
* @param {String} [opt.key='time'] - 用於排序與比對的 key
* @param {Number} [opt.timeInterval=20000] - 同步觸發間隔時間(毫秒),預設每 20 秒執行一次(1 分鐘 3 次)
* @param {boolean} [opt.useShowLog=false] - 是否顯示 log 資訊
* @returns {EventEmitter}
*/
function WDataSyncer(src, tar, opt = {}) {
//key
let key = get(opt, 'key')
if (!isestr(key)) {
key = 'time'
}
//timeInterval
let timeInterval = get(opt, 'timeInterval')
if (!ispint(timeInterval)) {
timeInterval = 20000
}
timeInterval = cint(timeInterval)
//waitEmitInsert
let waitEmitInsert = get(opt, 'waitEmitInsert')
if (!isbol(waitEmitInsert)) {
waitEmitInsert = false
}
//waitEmitSave
let waitEmitSave = get(opt, 'waitEmitSave')
if (!isbol(waitEmitSave)) {
waitEmitSave = false
}
//waitDel
let waitDel = get(opt, 'waitDel')
if (!isbol(waitDel)) {
waitDel = false
}
//waitEmitChange
let waitEmitChange = get(opt, 'waitEmitChange')
if (!isbol(waitEmitChange)) {
waitEmitChange = false
}
//waitEmitChangeAll
let waitEmitChangeAll = get(opt, 'waitEmitChangeAll')
if (!isbol(waitEmitChangeAll)) {
waitEmitChangeAll = false
}
//useShowLog
let useShowLog = get(opt, 'useShowLog')
if (!isbol(useShowLog)) {
useShowLog = false
}
//ev
let ev = evem()
//compareAndSync
let compareAndSync = async () => {
try {
//ltdtSrc
if (useShowLog) {
console.log(`src select...`)
}
let ltdtSrc = await src.select()
if (useShowLog) {
console.log(`src select done`)
}
// console.log('ltdtSrc', ltdtSrc)
//check
if (!isearr(ltdtSrc)) {
if (useShowLog) {
console.log(`no src, skip and waiting...`)
}
return
}
//ltdtTar
if (useShowLog) {
console.log(`tar select...`)
}
let ltdtTar = await tar.select()
if (useShowLog) {
console.log(`tar select done`)
}
// console.log('ltdtTar', ltdtTar)
//check
if (size(ltdtSrc) < size(ltdtTar)) {
if (useShowLog) {
console.log(`numSrc[${size(ltdtSrc)}] < numTar[${size(ltdtTar)}], skip and waiting...`)
}
return
}
if (useShowLog) {
console.log(`size(ltdtSrc)`, size(ltdtSrc))
console.log(`size(ltdtTar)`, size(ltdtTar))
}
//sortBy
if (useShowLog) {
console.log('sortBy...')
}
ltdtSrc = sortBy(ltdtSrc, key)
ltdtTar = sortBy(ltdtTar, key)
if (useShowLog) {
console.log('sortBy done')
}
//排序與建立map比對
if (useShowLog) {
console.log('ltdtDiffByKey...')
}
let r = ltdtDiffByKey(ltdtTar, ltdtSrc, key, { withInfor: false })
if (useShowLog) {
console.log('ltdtDiffByKey done')
}
// console.log('r', r)
let nAdd = size(r.add)
let nDiff = size(r.diff)
let nDel = size(r.del)
if (useShowLog) {
console.log('operate and emit...')
}
if (nAdd > 0) {
if (useShowLog) {
console.log('r.add', map(r.add, key), nAdd)
}
await tar.insert(r.add)
if (waitEmitInsert) {
let pm = genPm()
ev.emit('insert', r.add, pm)
await pm
.catch(() => {})
}
else {
ev.emit('insert', r.add)
}
if (waitEmitChange) {
let pm = genPm()
ev.emit('change', { type: 'insert', items: r.add }, pm)
await pm
.catch(() => {})
}
else {
ev.emit('change', { type: 'insert', items: r.add })
}
}
if (nDiff > 0) {
if (useShowLog) {
console.log('r.diff', map(r.diff, key), nDiff)
}
await tar.save(r.diff)
if (waitEmitSave) {
let pm = genPm()
ev.emit('save', r.diff, pm)
await pm
.catch(() => {})
}
else {
ev.emit('save', r.diff)
}
if (waitEmitChange) {
let pm = genPm()
ev.emit('change', { type: 'save', items: r.diff }, pm)
await pm
.catch(() => {})
}
else {
ev.emit('change', { type: 'save', items: r.diff })
}
}
if (nDel > 0) {
if (useShowLog) {
console.log('r.del', map(r.del, key), nDel)
}
await tar.del(r.del)
if (waitDel) {
let pm = genPm()
ev.emit('del', r.del, pm)
await pm
.catch(() => {})
}
else {
ev.emit('del', r.del)
}
if (waitEmitChange) {
let pm = genPm()
ev.emit('change', { type: 'del', items: r.del }, pm)
await pm
.catch(() => {})
}
else {
ev.emit('change', { type: 'del', items: r.del })
}
}
if ((nAdd + nDiff + nDel) > 0) {
//更改type
if (true) {
let kp = {
add: 'insert',
diff: 'save',
del: 'del',
same: 'same',
}
let _r = {}
each(r, (ltdt, type) => {
if (haskey(kp, type)) {
type = kp[type]
}
else {
console.log('kp', kp)
throw new Error(`invalid type[${type}]`)
}
_r[type] = ltdt
})
r = _r
}
if (waitEmitChangeAll) {
let pm = genPm()
ev.emit('change-all', r, pm)
await pm
.catch(() => {})
}
else {
ev.emit('change-all', r)
}
}
if (useShowLog) {
console.log('operate and emit done')
}
}
catch (err) {
if (useShowLog) {
console.log(err)
}
ev.emit('error', err)
}
}
//timer
let lock = false
let timer = setInterval(() => {
//check
if (lock) {
if (useShowLog) {
console.log('locking...')
}
return
}
lock = true
//compareAndSync
if (useShowLog) {
console.log('compareAndSync...')
}
compareAndSync()
.catch((err) => {
if (useShowLog) {
console.log(err)
}
ev.emit('error', err)
})
.finally(() => {
if (useShowLog) {
console.log('compareAndSync done')
}
lock = false
})
}, timeInterval)
//clear
let clear = () => {
clearInterval(timer)
}
//save
ev.clear = clear
return ev
}
export default WDataSyncer
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.4</a> on Sat Jul 05 2025 15:28:53 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/polyfill.js"></script>
<script src="scripts/linenumber.js"></script>
</body>
</html>