import get from 'lodash-es/get.js'
import genPm from './genPm.mjs'
import isfun from './isfun.mjs'
import isnull from './isnull.mjs'
import isundefined from './isundefined.mjs'
/**
* 掛勾非同步(Promise)函數,可監聽或修改Promise的輸出入訊號
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/pmHook.test.mjs Github}
* @memberOf wsemi
* @param {Function} fun 輸入非同步Promise函數
* @param {Function} [cb=() => {}] 輸入回調函數,預設()={},cb函數之輸入為監聽到的資訊物件,欄位有mode與data,mode可為'before'、'afterThen'、'afterCatch'字串,而data則代表非同步函數的輸入或輸出資訊。若想於cb函數修改回傳,則由cb函數的輸入修改完回傳即可。例如收到msg={mode:'before',data:'123'},將msg.data='abc',再return msg.data
* @returns {Promise} 回傳為Promise,resolve為回傳成功結果,reject為回傳失敗訊息
* @example
*
* async function topAsync() {
*
* async function test1() {
* return new Promise((resolve, reject) => {
* let ms = []
*
* //使用resolve
* let pm = function (v1, v2) {
* return new Promise(function(resolve, reject) {
* ms.push(`resolve: v1=${v1}, v2=${v2}`)
* resolve(`resolve: v1=${v1}, v2=${v2}`)
* })
* }
*
* //針對before修改輸入
* let pmr = pmHook(pm, (msg) => {
* console.log('cb', msg)
* if (msg.mode === 'before') {
* //arguments有兩個輸入故得分開改
* msg.data[0] = '[modify input a]' + msg.data[0]
* msg.data[1] = '[modify input b]' + msg.data[1]
* return msg.data
* }
* })
*
* pmr('t1', 12.3)
* .then(function(msg) {
* console.log('t1 then', msg)
* ms.push('t1 then: ' + msg)
* })
* .catch(function(msg) {
* console.log('t1 catch', msg)
* ms.push('t1 catch: ' + msg)
* })
* .finally(function() {
* resolve(ms)
* })
*
* })
* }
* console.log('test1')
* let r1 = await test1()
* console.log(JSON.stringify(r1))
* // test1
* // cb { mode: 'before', data: [Arguments] { '0': 't1', '1': 12.3 } }
* // cb {
* // mode: 'afterThen',
* // data: 'resolve: v1=[modify input a]t1, v2=[modify input b]12.3'
* // }
* // t1 then resolve: v1=[modify input a]t1, v2=[modify input b]12.3
* // ["resolve: v1=[modify input a]t1, v2=[modify input b]12.3","t1 then: resolve: v1=[modify input a]t1, v2=[modify input b]12.3"]
*
* async function test2() {
* return new Promise((resolve, reject) => {
* let ms = []
*
* //使用resolve
* let pm = function (v1, v2) {
* return new Promise(function(resolve, reject) {
* ms.push(`resolve: v1=${v1}, v2=${v2}`)
* resolve(`resolve: v1=${v1}, v2=${v2}`)
* })
* }
*
* //針對afterThen修改輸出
* let pmr = pmHook(pm, (msg) => {
* console.log('cb', msg)
* if (msg.mode === 'afterThen') {
* //arguments有兩個輸入故得分開改
* msg.data = '[modify output]' + msg.data
* return msg.data
* }
* })
*
* pmr('t1', 12.3)
* .then(function(msg) {
* console.log('t1 then', msg)
* ms.push('t1 then: ' + msg)
* })
* .catch(function(msg) {
* console.log('t1 catch', msg)
* ms.push('t1 catch: ' + msg)
* })
* .finally(function() {
* resolve(ms)
* })
*
* })
* }
* console.log('test2')
* let r2 = await test2()
* console.log(JSON.stringify(r2))
* // test2
* // cb { mode: 'before', data: [Arguments] { '0': 't1', '1': 12.3 } }
* // cb { mode: 'afterThen', data: 'resolve: v1=t1, v2=12.3' }
* // t1 then [modify output]resolve: v1=t1, v2=12.3
* // ["resolve: v1=t1, v2=12.3","t1 then: [modify output]resolve: v1=t1, v2=12.3"]
*
* async function test3() {
* return new Promise((resolve, reject) => {
* let ms = []
*
* //使用reject
* let pm = function (v1, v2) {
* return new Promise(function(resolve, reject) {
* ms.push(`reject: v1=${v1}, v2=${v2}`)
* reject(`reject: v1=${v1}, v2=${v2}`)
* })
* }
*
* //針對afterThen修改輸出, 但因使用reject故改不到
* let pmr = pmHook(pm, (msg) => {
* console.log('cb', msg)
* if (msg.mode === 'afterThen') {
* //arguments有兩個輸入故得分開改
* msg.data = '[modify output]' + msg.data
* return msg.data
* }
* })
*
* pmr('t1', 12.3)
* .then(function(msg) {
* console.log('t1 then', msg)
* ms.push('t1 then: ' + msg)
* })
* .catch(function(msg) {
* console.log('t1 catch', msg)
* ms.push('t1 catch: ' + msg)
* })
* .finally(function() {
* resolve(ms)
* })
*
* })
* }
* console.log('test3')
* let r3 = await test3()
* console.log(JSON.stringify(r3))
* // test3
* // cb { mode: 'before', data: [Arguments] { '0': 't1', '1': 12.3 } }
* // cb { mode: 'afterCatch', data: 'reject: v1=t1, v2=12.3' }
* // t1 catch reject: v1=t1, v2=12.3
* // ["reject: v1=t1, v2=12.3","t1 catch: reject: v1=t1, v2=12.3"]
*
* async function test4() {
* return new Promise((resolve, reject) => {
* let ms = []
*
* //使用reject
* let pm = function (v1, v2) {
* return new Promise(function(resolve, reject) {
* ms.push(`reject: v1=${v1}, v2=${v2}`)
* reject(`reject: v1=${v1}, v2=${v2}`)
* })
* }
*
* //針對afterCatch修改輸出
* let pmr = pmHook(pm, (msg) => {
* console.log('cb', msg)
* if (msg.mode === 'afterCatch') {
* //arguments有兩個輸入故得分開改
* msg.data = '[modify output]' + msg.data
* return msg.data
* }
* })
*
* pmr('t1', 12.3)
* .then(function(msg) {
* console.log('t1 then', msg)
* ms.push('t1 then: ' + msg)
* })
* .catch(function(msg) {
* console.log('t1 catch', msg)
* ms.push('t1 catch: ' + msg)
* })
* .finally(function() {
* resolve(ms)
* })
*
* })
* }
* console.log('test4')
* let r4 = await test4()
* console.log(JSON.stringify(r4))
* // test4
* // cb { mode: 'before', data: [Arguments] { '0': 't1', '1': 12.3 } }
* // cb { mode: 'afterCatch', data: 'reject: v1=t1, v2=12.3' }
* // t1 catch [modify output]reject: v1=t1, v2=12.3
* // ["reject: v1=t1, v2=12.3","t1 catch: [modify output]reject: v1=t1, v2=12.3"]
*
* async function test5() {
* return new Promise((resolve, reject) => {
* let ms = []
*
* //使用resolve, 此函數無輸入
* let pm = function () {
* return new Promise(function(resolve, reject) {
* ms.push(`resolve`)
* resolve(`resolve`)
* })
* }
*
* //針對afterThen修改輸出
* let pmr = pmHook(pm, (msg) => {
* console.log('cb', msg)
* if (msg.mode === 'afterThen') {
* //arguments有兩個輸入故得分開改
* msg.data = '[modify output]' + msg.data
* return msg.data
* }
* })
*
* pmr()
* .then(function(msg) {
* console.log('t1 then', msg)
* ms.push('t1 then: ' + msg)
* })
* .catch(function(msg) {
* console.log('t1 catch', msg)
* ms.push('t1 catch: ' + msg)
* })
* .finally(function() {
* resolve(ms)
* })
*
* })
* }
* console.log('test5')
* let r5 = await test5()
* console.log(JSON.stringify(r5))
* // test5
* // cb { mode: 'before', data: [Arguments] {} }
* // cb { mode: 'afterThen', data: 'resolve' }
* // t1 then [modify output]resolve
* // ["resolve","t1 then: [modify output]resolve"]
*
* }
* topAsync().catch(() => {})
*
*/
function pmHook(fun, cb = () => {}) {
//check
if (!isfun(fun)) {
return null
}
if (!isfun(cb)) {
cb = () => {}
}
function getData(newData, oriData) {
let r
if (!isnull(newData) && !isundefined(newData)) {
r = get(newData, 'data', null)
if (r) {
return r //若回傳是包成原始物件格式, 則取出data才回傳
}
return newData //若回傳是有效的新數據, 直接回傳
}
return oriData //若回傳為無效數據, 則回傳原始數據
}
return function() {
let r
//pm
let pm = genPm()
//callback before
let input = cb({
mode: 'before',
data: arguments,
})
//check
if (isnull(input) || isundefined(input)) {
input = arguments
}
//call fun with input
fun(...input)
.then((output) => {
r = cb({
mode: 'afterThen',
data: output,
})
output = getData(r, output)
pm.resolve(output)
})
.catch((output) => {
r = cb({
mode: 'afterCatch',
data: output,
})
output = getData(r, output)
pm.reject(output)
})
// //pxy
// let pxy = new Proxy(fun(input), {
// get(target, prop, receiver) {
// //console.log('target=', target, ', prop=', prop, ', receiver=', receiver)
// let value = Reflect.get(...arguments)
// target
// .then((output) => {
// cb({
// mode: 'afterThen',
// data: output,
// })
// })
// .catch((output) => {
// cb({
// mode: 'afterCatch',
// data: output,
// })
// })
// return typeof value === 'function' ? value.bind(target) : value
// }
// })
return pm
}
}
export default pmHook