Skip to content

Commit 76addba

Browse files
committed
Refactor function names and clean up code
1 parent 8f21e77 commit 76addba

File tree

6 files changed

+171
-92
lines changed

6 files changed

+171
-92
lines changed

.vscode/launch.json

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@
1919
"windows": {
2020
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
2121
}
22+
},
23+
{
24+
"name": "Jest all",
25+
"type": "node",
26+
"request": "launch",
27+
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/jest",
28+
"args": [
29+
"--runInBand",
30+
"--watch",
31+
"--coverage=false",
32+
"--no-cache"
33+
],
34+
"cwd": "${workspaceRoot}",
35+
"console": "integratedTerminal",
36+
"internalConsoleOptions": "openOnFirstSessionStart",
37+
"sourceMaps": true,
38+
"windows": {
39+
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
2240
}
23-
]
41+
}
42+
]
2443
}

src/TryCatchFinallyHooks.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,15 @@ export class TryCatchFinallyHooksBuilder<THookContext extends {}, TDecoratorArgs
7373
const _this = this
7474
const beforeHooksTry = this.beforeHooksTry.bind(this)
7575
const afterHooksTry = this.afterHooksTry?.bind(this)
76+
type TContext = THookContext & FunctionContext
7677
return (func:any)=>{
7778
return createTryCatchFinally(func, {
78-
onTry(funcArgs) {
79-
const ctx = { funcArgs, args }
80-
const bht = beforeHooksTry(ctx as any)
81-
const hooksRes = _this.forEachHook(hook=> hook.onTry( ctx as any))
79+
onTry(funcCtx) {
80+
const ctx:TContext = funcCtx as any
81+
if(args)
82+
(ctx as any).args = args
83+
const bht = beforeHooksTry(ctx)
84+
const hooksRes = _this.forEachHook(hook=> hook.onTry(ctx))
8285
for (const hookRes of [...hooksRes]) {
8386
if(hookRes && hookRes.lastInQueue){
8487
const [itemToMove] =hooksRes.splice(hooksRes.indexOf(hookRes), 1)

src/example/tracker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const track = (() => {
3232
const myTrackedFunction = track.asFunctionWrapper({ name: 'MyAction' })(
3333
function myTrackedFunction(a: number, b: number) {
3434
track.current!.defer((op) => {
35-
console.log('Defered action invoked at finally section of MyAction! Outcome:', op.outcome);
35+
console.log('Defered action invoked at finally section of MyAction! Outcome:', op.funcOutcome);
3636
});
3737

3838

src/node/callStack.test.ts

Lines changed: 70 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ function createTrack(log:any){
77
.add(callStack)
88
.add({
99
onTry(ctx) {
10-
log("onTry",ctx.args.name)
10+
log("onTry", [...ctx.callstack.actions.map(c=>c.args.name), ctx.args.name].join('/'))
1111
return {
1212
onFinally() {
13-
log("onFinally",ctx.callstack.map(c=>c.args.name).join("/"))
13+
log("onFinally",[...ctx.callstack.actions.map(c=>c.args.name), ctx.args.name].join('/'))
1414
},
1515
onCatch() {
16-
log("onCatch",ctx.callstack.map(c=>c.args.name).join("/"))
16+
log("onCatch",[...ctx.callstack.actions.map(c=>c.args.name), ctx.args.name].join('/'))
1717
}
1818
}
1919
}
@@ -47,13 +47,14 @@ test("callstack", ()=>{
4747
})
4848

4949
test("callstack async",async ()=>{
50+
const amountOfParallels = 2
5051
const log = jest.fn((...args:any[])=>console.log("async",...args))
5152
const track = createTrack(log)
5253

5354
const asyncStr = new AsyncLocalStorage<any>()
5455
asyncStr.enterWith(["root"])
5556

56-
const myChildFunc = jest.fn(track.asFunctionWrapper({name:"MyAsyncChildFunc"})(async (a:number,b:number)=>{
57+
const myChildFunc = jest.fn(track.asFunctionWrapper({name:"MyAsyncChildFunc"})(async function myChildFunc(a:number,b:number){
5758
const path = [...asyncStr.getStore()||[]]
5859
asyncStr.enterWith([...path,"child"])
5960
await delay(Math.random()*1000)
@@ -63,95 +64,105 @@ test("callstack async",async ()=>{
6364
}))
6465

6566

66-
const myParentFunc = jest.fn(track.asFunctionWrapper({name: 'MyAsyncParentFunc'})(()=>{
67+
const myParentFunc = jest.fn(track.asFunctionWrapper({name: 'MyAsyncParentFunc'})(async function myParentFunc(){
6768
const path = [...asyncStr.getStore()||[]]
6869
asyncStr.enterWith([...path,"parent"])
69-
return Promise.allSettled(new Array(2).fill(0).map(async (_,i)=>{
70+
return await Promise.allSettled(new Array(amountOfParallels).fill(0).map(async function promiseAllRunner(_,i){
7071
await myChildFunc(i,i*2)
7172
}))
7273
}))
7374

7475

7576
await myParentFunc()
7677

77-
expect(myChildFunc).toHaveBeenCalledTimes(10)
78+
expect(myChildFunc).toHaveBeenCalledTimes(amountOfParallels)
7879

79-
expect(log).toHaveBeenCalledTimes(22)
80+
expect(log).toHaveBeenCalledTimes((amountOfParallels+1)*2)
8081
expect(log).toHaveBeenCalledWith("onTry", "MyAsyncParentFunc")
8182
expect(log).toHaveBeenCalledWith("onTry", "MyAsyncParentFunc/MyAsyncChildFunc")
8283
})
8384

8485
function delay(ms:number){return new Promise(r=>setTimeout(r,ms))}
8586

87+
test.only("callstack recursive fiboncci", ()=>{
88+
const log = jest.fn((...args:any[])=>console.log(...args))
89+
const track = createTrack(log)
8690

87-
test('async hooks',async ()=>{
88-
const callStack = new AsyncLocalStorage<{name:string}[]>()
89-
callStack.enterWith([{name:"parent"}])
90-
await delay(100)
91-
expect(callStack.getStore()).toEqual([{name:"parent"}])
92-
callStack.enterWith([{name:"parent"}, {name:'child'}])
93-
await delay(100)
94-
expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
95-
await delay(100)
91+
const fib = jest.fn(track.asFunctionWrapper({name:"fib"})(function(n:number):number{
92+
if(n<=1) return 1
93+
return fib(n-1)+fib(n-2)
94+
}))
9695

96+
const actRes =fib(3)
97+
const expRes = 1+ 1+2 + 1+2+3
98+
expect(actRes).toBe(expRes)
99+
expect(fib).toHaveBeenCalledTimes(1+2+3)
100+
expect(log).toHaveBeenCalledTimes((1+2+3)*2)
101+
})
97102

98-
async function myAsyncFunc(){
99-
await delay(500)
100-
expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
101-
callStack.enterWith([...callStack.getStore()!, {name:"myAsyncFunc"}])
102-
await delay(1000)
103-
expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}, {name:"myAsyncFunc"}])
104-
}
103+
// test.skip('async hooks',async ()=>{
104+
// const callStack = new AsyncLocalStorage<{name:string}[]>()
105+
// callStack.enterWith([{name:"parent"}])
106+
// await delay(100)
107+
// expect(callStack.getStore()).toEqual([{name:"parent"}])
108+
// callStack.enterWith([{name:"parent"}, {name:'child'}])
109+
// await delay(100)
110+
// expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
111+
// await delay(100)
105112

106-
async function myAsyncFunc2(){
107-
await delay(1000)
108-
expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
109-
callStack.enterWith([...callStack.getStore()!, {name:"myAsyncFunc2"}])
110-
await delay(1000)
111-
expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}, {name:"myAsyncFunc2"}])
112-
}
113113

114+
// async function myAsyncFunc(){
115+
// await delay(500)
116+
// expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
117+
// callStack.enterWith([...callStack.getStore()!, {name:"myAsyncFunc"}])
118+
// await delay(1000)
119+
// expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}, {name:"myAsyncFunc"}])
120+
// }
114121

115-
await Promise.allSettled([myAsyncFunc(), myAsyncFunc2()])
122+
// async function myAsyncFunc2(){
123+
// await delay(1000)
124+
// expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
125+
// callStack.enterWith([...callStack.getStore()!, {name:"myAsyncFunc2"}])
126+
// await delay(1000)
127+
// expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}, {name:"myAsyncFunc2"}])
128+
// }
116129

117-
expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
118130

131+
// await Promise.allSettled([myAsyncFunc(), myAsyncFunc2()])
119132

120-
})
133+
// expect(callStack.getStore()).toEqual([{name:"parent"},{name:"child"}])
121134

122-
test.only('async hooks array',async ()=>{
123-
const callStack = new AsyncLocalStorage<string[]>()
124-
callStack.enterWith(["parent"])
125-
await delay(100)
126-
expect(callStack.getStore()).toEqual(["parent"])
127-
await delay(100)
128135

136+
// })
129137

130-
const stack = {} as any
131-
Error.captureStackTrace(stack)
132-
console.log("stack",stack.stack)
138+
// test.skip('async hooks array',async ()=>{
139+
// const callStack = new AsyncLocalStorage<string[]>()
140+
// callStack.enterWith(["parent"])
141+
// await delay(100)
142+
// expect(callStack.getStore()).toEqual(["parent"])
143+
// await delay(100)
133144

134145

135-
async function myAsyncFunc(n: number){
136-
const actionName = "myAsyncFunc"+n
137-
//await delay(0)
138-
const storeBeforeAwait = callStack.getStore()!
139-
//expect(storeBeforeAwait).toEqual(["parent"])
140-
callStack.enterWith([...storeBeforeAwait, actionName])
146+
// async function myAsyncFunc(n: number){
147+
// const actionName = "myAsyncFunc"+n
148+
// //await delay(0)
149+
// const storeBeforeAwait = callStack.getStore()!
150+
// //expect(storeBeforeAwait).toEqual(["parent"])
151+
// callStack.enterWith([...storeBeforeAwait, actionName])
141152

142-
await delay(Math.random()*1000)
153+
// await delay(Math.random()*1000)
143154

144-
const storeAfterAwait = callStack.getStore()!
145-
expect(storeAfterAwait).toEqual([...storeBeforeAwait,actionName])
146-
const newStack = [...storeAfterAwait]
147-
newStack.pop()
148-
callStack.enterWith(newStack)
149-
}
155+
// const storeAfterAwait = callStack.getStore()!
156+
// expect(storeAfterAwait).toEqual([...storeBeforeAwait,actionName])
157+
// const newStack = [...storeAfterAwait]
158+
// newStack.pop()
159+
// callStack.enterWith(newStack)
160+
// }
150161

151162

152-
await Promise.all(new Array(2).fill(0).map((_,i)=>myAsyncFunc(i)))
163+
// await Promise.all(new Array(2).fill(0).map((_,i)=>myAsyncFunc(i)))
153164

154-
expect(callStack.getStore()).toEqual(["parent"])
165+
// expect(callStack.getStore()).toEqual(["parent"])
155166

156167

157-
})
168+
// })

src/node/callStack.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,61 @@ import { ITryCatchFinallyHook } from "../TryCatchFinallyHooks";
22
import {AsyncLocalStorage} from 'async_hooks'
33

44

5-
type CallstackContext = { args: { name: string; }; callstack: CallstackContext[]; };
5+
const callPrefix = 'callstack:'
6+
Error.stackTraceLimit = 100
7+
8+
type CallstackContext = { args: { name: string;}, callstack: { id: string, actions: CallstackContext[] }; };
69
let globalCallstack: AsyncLocalStorage<CallstackContext[]> = new AsyncLocalStorage();
710
export const callStack: ITryCatchFinallyHook<CallstackContext> = {
811
onTry(ctx) {
12+
const oldWrapperName = ctx.funcWrapper.name;
13+
const callId = Math.random().toString(16).slice(2)
14+
Object.defineProperty(ctx.funcWrapper, 'name', { value: callPrefix + callId})
15+
const fullstack = captureStackTrace()!
16+
const actionCallIdStack = fullstack.filter(s=>s.getFunctionName()?.startsWith(callPrefix)).map(n=>n.getFunctionName()?.slice(callPrefix.length))
17+
918
const prevCallstack = [...globalCallstack.getStore()||[]];
19+
let newStack = [...prevCallstack, ctx];
20+
globalCallstack.enterWith(newStack);
1021

11-
ctx.callstack = [...prevCallstack, ctx];
12-
globalCallstack.enterWith(ctx.callstack);
22+
ctx.callstack={id: callId, actions: newStack.filter(sctx=> actionCallIdStack.includes(sctx.callstack?.id))}
1323
return {
1424
onFinally() {
15-
globalCallstack.enterWith(prevCallstack);
25+
//globalCallstack.enterWith(prevCallstack);
26+
Object.defineProperty(ctx.funcWrapper, 'name', { value: oldWrapperName })
27+
const glob = globalCallstack.getStore()!
28+
if(glob.includes(ctx)) glob.splice(glob.indexOf(ctx),1)
29+
globalCallstack.enterWith(glob)
1630
},
1731
lastInQueue: true
1832
};
1933
}
2034
};
35+
36+
function captureStackTrace(){
37+
const prepStackOld = Error.prepareStackTrace
38+
// let res: NodeJS.CallSite[] = []
39+
try
40+
{
41+
Error.prepareStackTrace = (err, stack)=>{
42+
//throw 'zzz'
43+
const protoKeys = Reflect.ownKeys(Object.getPrototypeOf(stack[0])).filter(k=>k!='constructor'&&k!='toString')
44+
45+
const stackRes = stack.map(s=>{
46+
const sres: any = {}
47+
for (const k of protoKeys) {
48+
const v = (s as any)[k]
49+
if(typeof v === 'function') sres['_'+k.toString()]=v.call(s)
50+
}
51+
return Object.assign(s, sres)
52+
})
53+
return stackRes
54+
}
55+
const s: {stack?: NodeJS.CallSite[] } = {}
56+
Error.captureStackTrace(s)
57+
return s.stack?.shift(), s.stack
58+
}
59+
finally{
60+
Error.prepareStackTrace = prepStackOld
61+
}
62+
}

0 commit comments

Comments
 (0)