import get from 'lodash-es/get.js'
import ispint from './ispint.mjs'
import cint from './cint.mjs'
import delay from './delay.mjs'
/**
* 非同步函數快取,封裝指定非同步函數與再提供取用快取版
*
* get須定期或高執行率,getFromCache雖能自己自動調用,但僅第1次取得後數據就無法更新,此會導致一直取得舊數據
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/cacheBd.test.mjs Github}
* @memberOf wsemi
* @param {Function} fun 輸入非同步函數
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Integer} [nMax=200] 輸入使用快取版之總嘗試次數整數,超過則報錯,預設200
* @param {Integer} [nTrigger=3] 輸入嘗試指定次數整數,若嘗試指定次數之後皆無快取則須自動執行get用以取得快取,預設3
* @returns {Object} 回傳事件物件,可呼叫事件get、getFromCache,get為封裝後封裝原本非同步函數,getFromCache為提供取用快取版之非同步函數,若有快取則優先使用,若超過指定次數則自動執行get使能取得快取
* @example
*
* let test1 = async () => {
* let ms = []
*
* let fun = async(p1, p2) => {
* await delay(300)
* return `${p1}:${p2}`
* }
*
* let bc = cacheBd(fun)
* let execFun = bc.get
* let execFunCache = bc.getFromCache
*
* let pm1 = execFunCache(123, 'abc') //pm1執行取得快取, 因無快取將持續等待
* pm1
* .then((res) => {
* // console.log('res', res)
* ms.push({ result: `pm1-${res}` })
* })
*
* let pm2 = execFun(4.56, 'def') //pm2執行原本函數與使用輸入, 會最先回傳, 且更新快取給等待或之後取得快取使用
* pm2
* .then((res) => {
* // console.log('res', res)
* ms.push({ result: `pm2-${res}` })
* })
*
* let pm3 = execFunCache(78.9, 'xyz') //pm3執行取得快取, 因已有快取將直接取得快取
* pm3
* .then((res) => {
* // console.log('res', res)
* ms.push({ result: `pm3-${res}` })
* })
*
* await Promise.all([pm1, pm2, pm3])
* // console.log('rs', rs)
*
* console.log('ms', ms)
* return ms
* }
* await test1()
* // ms [
* // { result: 'pm2-4.56:def' },
* // { result: 'pm1-4.56:def' },
* // { result: 'pm3-4.56:def' }
* // ]
*
* let test2 = async () => {
* let ms = []
*
* let fun = async(p1, p2) => {
* await delay(300)
* return `${p1}:${p2}`
* }
*
* let bc = cacheBd(fun)
* let execFun = bc.get
* let execFunCache = bc.getFromCache
*
* setTimeout(() => {
* console.log('pm1 exec..')
* let pm1 = execFunCache(123, 'abc') //pm1執行取得快取, 因無快取將持續等待
* pm1
* .then((res) => {
* console.log('pm1 then', res)
* ms.push({ result: `pm1-${res}` })
* })
* }, 1)
*
* setTimeout(() => {
* console.log('pm2 exec..')
* let pm2 = execFun(4.56, 'def') //1000ms後, pm2執行原本函數與使用輸入, 會最先回傳, 且更新快取給等待或之後取得快取使用
* pm2
* .then((res) => {
* console.log('pm2 then', res)
* ms.push({ result: `pm2-${res}` })
* })
* }, 1000)
*
* setTimeout(() => {
* console.log('pm3 exec..')
* let pm3 = execFunCache(78.9, 'xyz') //pm3執行取得快取, 因已有快取將直接取得快取
* pm3
* .then((res) => {
* console.log('pm3 then', res)
* ms.push({ result: `pm3-${res}` })
* })
* }, 3000)
*
* await delay(5000)
*
* console.log('ms', ms)
* return ms
* }
* await test2()
* // ms [
* // { result: 'pm2-4.56:def' },
* // { result: 'pm1-4.56:def' },
* // { result: 'pm3-4.56:def' }
* // ]
*
* let test3 = async () => {
* let ms = []
*
* let fun = async(p1, p2) => {
* await delay(300)
* return `${p1}:${p2}`
* }
*
* let bc = cacheBd(fun)
* let execFun = bc.get
* let execFunCache = bc.getFromCache
*
* setTimeout(() => {
* console.log('pm1 exec..')
* let pm1 = execFunCache(123, 'abc') //pm1執行取得快取, 因無快取將持續等待, 3000ms將執行get與自己的輸入, 並最先回傳
* pm1
* .then((res) => {
* console.log('pm1 then', res)
* ms.push({ result: `pm1-${res}` })
* })
* }, 1)
*
* setTimeout(() => {
* console.log('pm2 exec..')
* let pm2 = execFun(4.56, 'def') //3500ms後, pm2執行原本函數與使用輸入, 計算完畢則會覆蓋快取, 此快取將再給等待或之後取得快取使用
* pm2
* .then((res) => {
* console.log('pm2 then', res)
* ms.push({ result: `pm2-${res}` })
* })
* }, 3500)
*
* setTimeout(() => {
* console.log('pm3 exec..')
* let pm3 = execFunCache(78.9, 'xyz') //pm3執行取得快取, 因已有快取將直接取得快取
* pm3
* .then((res) => {
* console.log('pm3 then', res)
* ms.push({ result: `pm3-${res}` })
* })
* }, 4500)
*
* await delay(6500)
*
* console.log('ms', ms)
* return ms
* }
* await test3()
* // ms [
* // { result: 'pm1-123:abc' },
* // { result: 'pm2-4.56:def' },
* // { result: 'pm3-4.56:def' }
* // ]
*
*/
function cacheBd(fun, opt = {}) {
//nMax
let nMax = get(opt, 'nMax')
if (!ispint(nMax)) {
nMax = 200
}
nMax = cint(nMax)
//nTrigger
let nTrigger = get(opt, 'nTrigger')
if (!ispint(nTrigger)) {
nTrigger = 3
}
nTrigger = cint(nTrigger)
//快取
let cc = null
//函數執行狀態
let lock = false
//_get, 封裝原本非同步函數
let _get = async(...inputs) => {
lock = true
cc = await fun(...inputs) //若發生錯誤則向外報錯
lock = false
return cc
}
//_getFromCache, 提供取用快取版之非同步函數, 若有快取則優先使用, 若超過指定次數則執行get使能取得快取
let _getFromCache = async(...inputs) => {
for (let i = 1; i <= nMax; i++) {
//exec
if (i > nTrigger && !lock) {
// console.log('執行get使能取得快取...')
await _get(...inputs)
.catch(() => {}) //取得快取時不向外報錯
}
//check
if (cc !== null) {
break
}
//delay
await delay(1000) //偵測週期1000ms
}
if (cc === null) {
throw new Error(`exceeded nMax[${nMax}]`)
}
return cc
}
return {
get: _get,
getFromCache: _getFromCache,
}
}
export default cacheBd