Skip to content

Commit 53d1d5f

Browse files
authored
fix: batch console logs by microtask (#7183)
1 parent 1059850 commit 53d1d5f

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

packages/utils/src/timers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface SafeTimers {
88
clearTimeout: typeof clearTimeout
99
setImmediate: typeof setImmediate
1010
clearImmediate: typeof clearImmediate
11+
queueMicrotask: typeof queueMicrotask
1112
}
1213

1314
export function getSafeTimers(): SafeTimers {
@@ -18,6 +19,7 @@ export function getSafeTimers(): SafeTimers {
1819
clearTimeout: safeClearTimeout,
1920
setImmediate: safeSetImmediate,
2021
clearImmediate: safeClearImmediate,
22+
queueMicrotask: safeQueueMicrotask,
2123
} = (globalThis as any)[SAFE_TIMERS_SYMBOL] || globalThis
2224

2325
const { nextTick: safeNextTick } = (globalThis as any)[SAFE_TIMERS_SYMBOL]
@@ -31,6 +33,7 @@ export function getSafeTimers(): SafeTimers {
3133
clearTimeout: safeClearTimeout,
3234
setImmediate: safeSetImmediate,
3335
clearImmediate: safeClearImmediate,
36+
queueMicrotask: safeQueueMicrotask,
3437
}
3538
}
3639

@@ -42,6 +45,7 @@ export function setSafeTimers(): void {
4245
clearTimeout: safeClearTimeout,
4346
setImmediate: safeSetImmediate,
4447
clearImmediate: safeClearImmediate,
48+
queueMicrotask: safeQueueMicrotask,
4549
} = globalThis
4650

4751
const { nextTick: safeNextTick } = globalThis.process || {
@@ -56,6 +60,7 @@ export function setSafeTimers(): void {
5660
clearTimeout: safeClearTimeout,
5761
setImmediate: safeSetImmediate,
5862
clearImmediate: safeClearImmediate,
63+
queueMicrotask: safeQueueMicrotask,
5964
};
6065

6166
(globalThis as any)[SAFE_TIMERS_SYMBOL] = timers

packages/vitest/src/runtime/console.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,31 @@ export function createCustomConsole(defaultState?: WorkerGlobalState) {
3737
const stderrBuffer = new Map<string, any[]>()
3838
const timers = new Map<
3939
string,
40-
{ stdoutTime: number; stderrTime: number; timer: any }
40+
{ stdoutTime: number; stderrTime: number; cancel?: () => void }
4141
>()
4242

43-
const { setTimeout, clearTimeout } = getSafeTimers()
43+
const { queueMicrotask } = getSafeTimers()
44+
45+
function queueCancelableMicrotask(callback: () => void) {
46+
let canceled = false
47+
queueMicrotask(() => {
48+
if (!canceled) {
49+
callback()
50+
}
51+
})
52+
return () => {
53+
canceled = true
54+
}
55+
}
4456

4557
const state = () => defaultState || getWorkerState()
4658

47-
// group sync console.log calls with macro task
59+
// group sync console.log calls with micro task
4860
function schedule(taskId: string) {
4961
const timer = timers.get(taskId)!
5062
const { stdoutTime, stderrTime } = timer
51-
clearTimeout(timer.timer)
52-
timer.timer = setTimeout(() => {
63+
timer.cancel?.()
64+
timer.cancel = queueCancelableMicrotask(() => {
5365
if (stderrTime < stdoutTime) {
5466
sendStderr(taskId)
5567
sendStdout(taskId)
@@ -128,7 +140,6 @@ export function createCustomConsole(defaultState?: WorkerGlobalState) {
128140
timer = {
129141
stdoutTime: RealDate.now(),
130142
stderrTime: RealDate.now(),
131-
timer: 0,
132143
}
133144
timers.set(id, timer)
134145
}
@@ -168,7 +179,6 @@ export function createCustomConsole(defaultState?: WorkerGlobalState) {
168179
timer = {
169180
stderrTime: RealDate.now(),
170181
stdoutTime: RealDate.now(),
171-
timer: 0,
172182
}
173183
timers.set(id, timer)
174184
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { afterAll, afterEach, beforeAll, beforeEach, describe, test } from 'vitest'
2+
3+
beforeAll(() => {
4+
console.log('__TEST__ [beforeAll 1]')
5+
})
6+
beforeAll(() => {
7+
console.log('__TEST__ [beforeAll 2]')
8+
})
9+
10+
afterAll(() => {
11+
console.log('__TEST__ [afterAll 1]')
12+
})
13+
afterAll(() => {
14+
console.log('__TEST__ [afterAll 2]')
15+
})
16+
17+
beforeEach(() => {
18+
console.log('__TEST__ [beforeEach 1]')
19+
})
20+
beforeEach(() => {
21+
console.log('__TEST__ [beforeEach 2]')
22+
})
23+
24+
afterEach(() => {
25+
console.log('__TEST__ [afterEach 1]')
26+
})
27+
afterEach(() => {
28+
console.log('__TEST__ [afterEach 2]')
29+
})
30+
31+
test('test', async () => {
32+
console.log('__TEST__ [test 1]')
33+
console.log('__TEST__ [test 2]')
34+
await Promise.resolve()
35+
console.log('__TEST__ [test 3]')
36+
console.log('__TEST__ [test 4]')
37+
})

test/config/test/console.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,47 @@ test.each(['threads', 'vmThreads'] as const)(`disable intercept pool=%s`, async
2121
const call = spy.mock.lastCall![0]
2222
expect(call.toString()).toBe('__test_console__\n')
2323
})
24+
25+
test('group synchronous console logs', async () => {
26+
const { stdout } = await runVitest({
27+
root: './fixtures/console-batch',
28+
})
29+
const logs = stdout
30+
.split('\n')
31+
.filter(row => row.length === 0 || row.startsWith('stdout | ') || row.startsWith('__TEST__'))
32+
.join('\n')
33+
.trim()
34+
expect(logs).toMatchInlineSnapshot(`
35+
"stdout | basic.test.ts
36+
__TEST__ [beforeAll 1]
37+
38+
stdout | basic.test.ts
39+
__TEST__ [beforeAll 2]
40+
41+
stdout | basic.test.ts > test
42+
__TEST__ [beforeEach 1]
43+
44+
stdout | basic.test.ts > test
45+
__TEST__ [beforeEach 2]
46+
47+
stdout | basic.test.ts > test
48+
__TEST__ [test 1]
49+
__TEST__ [test 2]
50+
51+
stdout | basic.test.ts > test
52+
__TEST__ [test 3]
53+
__TEST__ [test 4]
54+
55+
stdout | basic.test.ts > test
56+
__TEST__ [afterEach 2]
57+
58+
stdout | basic.test.ts > test
59+
__TEST__ [afterEach 1]
60+
61+
stdout | basic.test.ts
62+
__TEST__ [afterAll 2]
63+
64+
stdout | basic.test.ts
65+
__TEST__ [afterAll 1]"
66+
`)
67+
})

0 commit comments

Comments
 (0)