Skip to content

Commit f80e82a

Browse files
committed
single q
1 parent 0804dd1 commit f80e82a

File tree

10 files changed

+86
-82
lines changed

10 files changed

+86
-82
lines changed

packages/browser/src/__tests__/deferred-init-extensions.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createPosthogInstance } from './helpers/posthog-instance'
22
import { uuidv7 } from '../uuidv7'
33
import { RemoteConfig } from '../types'
4+
import { scheduler } from '../utils/scheduler'
45

56
jest.mock('../utils/globals', () => {
67
const orig = jest.requireActual('../utils/globals')
@@ -39,6 +40,7 @@ describe('deferred extension initialization', () => {
3940
console.error = jest.fn()
4041
mockReferrerGetter.mockReturnValue('https://referrer.com')
4142
mockURLGetter.mockReturnValue('https://example.com')
43+
scheduler._reset()
4244
})
4345

4446
describe('race condition handling', () => {
@@ -121,7 +123,7 @@ describe('deferred extension initialization', () => {
121123

122124
const posthog = await createPosthogInstance(token, {
123125
__preview_deferred_init_extensions: true,
124-
advanced_disable_decide: false,
126+
advanced_disable_decide: true,
125127
capture_pageview: false,
126128
disable_session_recording: true,
127129
})

packages/browser/src/entrypoints/array.full.es5.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import 'core-js/features/object/entries'
99
import 'core-js/features/object/from-entries'
10-
import 'core-js/features/promise'
1110

1211
if (typeof performance === 'undefined' || typeof performance.now !== 'function') {
1312
const perf = typeof performance !== 'undefined' ? performance : ({} as any)

packages/browser/src/extensions/replay/external/lazy-loaded-session-recorder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,7 @@ export class LazyLoadedSessionRecording implements LazyLoadedSessionRecordingInt
10701070

10711071
if (this._buffer.data.length > 0) {
10721072
const snapshotEvents = splitBuffer(this._buffer)
1073-
void scheduler.processEach(snapshotEvents, (snapshotBuffer) => {
1073+
scheduler.processEach(snapshotEvents, (snapshotBuffer) => {
10741074
this._flushedSizeTracker?.trackSize(snapshotBuffer.size)
10751075
this._captureSnapshot({
10761076
$snapshot_bytes: snapshotBuffer.size,

packages/browser/src/extensions/replay/external/network-plugin.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,19 @@ function initPerformanceObserver(cb: networkCallback, win: IWindow, options: Req
6363
(isResourceTiming(entry) && options.initiatorTypes.includes(entry.initiatorType as InitiatorType))
6464
)
6565

66-
// Process initial performance entries with yielding for large sets
67-
scheduler
68-
.processEach(initialPerformanceEntries, (entry) =>
69-
prepareRequest({ entry, method: undefined, status: undefined, networkRequest: {}, isInitial: true })
70-
)
71-
.then((requests) => {
72-
cb({
73-
requests: requests.flat(),
74-
isInitial: true,
75-
})
76-
})
66+
scheduler.processEach(
67+
initialPerformanceEntries,
68+
(entry) =>
69+
prepareRequest({ entry, method: undefined, status: undefined, networkRequest: {}, isInitial: true }),
70+
{
71+
onComplete: (requests) => {
72+
cb({
73+
requests: requests.flat(),
74+
isInitial: true,
75+
})
76+
},
77+
}
78+
)
7779
}
7880
const observer = new win.PerformanceObserver((entries) => {
7981
// if recordBody or recordHeaders is true then we don't want to record fetch or xhr here

packages/browser/src/posthog-core.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -735,22 +735,17 @@ export class PostHog {
735735
// we don't support IE11 anymore, so performance.now is safe
736736
// eslint-disable-next-line compat/compat
737737
const startTime = performance.now()
738-
scheduler
739-
.processEach(initTasks, (task) => task())
740-
.then(
741-
() => {
742-
// eslint-disable-next-line compat/compat
743-
const totalTimeMs = Math.round(performance.now() - startTime)
744-
this.register_for_session({
745-
$sdk_debug_extensions_init_method: 'deferred',
746-
$sdk_debug_extensions_init_time_ms: totalTimeMs,
747-
})
748-
logger.info(`PostHog extensions initialized (${totalTimeMs}ms)`)
749-
},
750-
(error) => {
751-
logger.error('Error initializing extension:', error)
752-
}
753-
)
738+
scheduler.processEach(initTasks, (task) => task(), {
739+
onComplete: () => {
740+
// eslint-disable-next-line compat/compat
741+
const totalTimeMs = Math.round(performance.now() - startTime)
742+
this.register_for_session({
743+
$sdk_debug_extensions_init_method: 'deferred',
744+
$sdk_debug_extensions_init_time_ms: totalTimeMs,
745+
})
746+
logger.info(`PostHog extensions initialized (${totalTimeMs}ms)`)
747+
},
748+
})
754749
} else {
755750
// we don't support IE11 anymore, so performance.now is safe
756751
// eslint-disable-next-line compat/compat

packages/browser/src/request-queue.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,14 @@ export class RequestQueue {
5858
if (this._isPaused) {
5959
return
6060
}
61-
this._flushTimeout = setTimeout(async () => {
61+
this._flushTimeout = setTimeout(() => {
6262
this._clearFlushTimeout()
6363
if (this._queue.length > 0) {
6464
const requests = this._formatQueue()
6565
const requestEntries = Object.entries(requests)
6666
const now = new Date().getTime()
6767

68-
// Process timestamp updates with yielding for large batches
69-
await scheduler.processEach(
68+
scheduler.processEach(
7069
requestEntries,
7170
([, req]) => {
7271
if (req.data && isArray(req.data)) {

packages/browser/src/retry-queue.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ export class RetryQueue {
121121
return
122122
}
123123

124-
this._poller = setTimeout(async () => {
124+
this._poller = setTimeout(() => {
125125
if (this._areWeOnline && this._queue.length > 0) {
126-
await this._flush()
126+
this._flush()
127127
}
128128
this._poll()
129129
}, this._pollIntervalMs) as any as number
130130
}
131131

132-
private async _flush(): Promise<void> {
132+
private _flush(): void {
133133
const now = Date.now()
134134
const notToFlush: RetryQueueElement[] = []
135135
const toFlush = this._queue.filter((item) => {
@@ -143,7 +143,7 @@ export class RetryQueue {
143143
this._queue = notToFlush
144144

145145
if (toFlush.length > 0) {
146-
await scheduler.processEach(toFlush, ({ requestOptions }) => this.retriableRequest(requestOptions), {
146+
scheduler.processEach(toFlush, ({ requestOptions }) => this.retriableRequest(requestOptions), {
147147
priority: 'high',
148148
})
149149
}

packages/browser/src/site-apps.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ export class SiteApps {
9393
const processBufferedEvents = () => {
9494
if (!app.errored && this._bufferedInvocations.length) {
9595
logger.info(`Processing ${this._bufferedInvocations.length} events for site app with id ${loader.id}`)
96-
void scheduler
97-
.processEach(this._bufferedInvocations, (globals) => app.processEvent?.(globals))
98-
.then(() => {
96+
scheduler.processEach(this._bufferedInvocations, (globals) => app.processEvent?.(globals), {
97+
onComplete: () => {
9998
app.processedBuffer = true
10099
if (Object.values(this.apps).every((app) => app.processedBuffer || app.errored)) {
101100
this._stopBuffering?.()
102101
}
103-
})
102+
},
103+
})
104104
}
105105

106106
if (Object.values(this.apps).every((app) => app.processedBuffer || app.errored)) {

packages/browser/src/utils/__tests__/scheduler.test.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable compat/compat */
21
import { scheduler } from '../scheduler'
32

43
describe('Scheduler', () => {
@@ -15,38 +14,52 @@ describe('Scheduler', () => {
1514
it.each([
1615
{ items: [1, 2, 3, 4, 5], expected: [2, 4, 6, 8, 10] },
1716
{ items: [10], expected: [20] },
18-
])('returns results in order for $items', async ({ items, expected }) => {
19-
const promise = scheduler.processEach(items, (x) => x * 2)
17+
])('returns results in order for $items', ({ items, expected }) => {
18+
let completedResults: number[] | undefined
19+
scheduler.processEach(items, (x) => x * 2, {
20+
onComplete: (results) => {
21+
completedResults = results
22+
},
23+
})
2024
jest.runAllTimers()
21-
expect(await promise).toEqual(expected)
25+
expect(completedResults).toEqual(expected)
2226
})
2327

24-
it('provides index to callback', async () => {
25-
const promise = scheduler.processEach(['a', 'b', 'c'], (item, index) => `${index}:${item}`)
28+
it('provides index to callback', () => {
29+
let completedResults: string[] | undefined
30+
scheduler.processEach(['a', 'b', 'c'], (item, index) => `${index}:${item}`, {
31+
onComplete: (results) => {
32+
completedResults = results
33+
},
34+
})
2635
jest.runAllTimers()
27-
expect(await promise).toEqual(['0:a', '1:b', '2:c'])
36+
expect(completedResults).toEqual(['0:a', '1:b', '2:c'])
2837
})
2938

30-
it('handles empty array', async () => {
31-
const results = await scheduler.processEach([], (x) => x)
32-
expect(results).toEqual([])
39+
it('handles empty array', () => {
40+
let completedResults: unknown[] | undefined
41+
scheduler.processEach([], (x) => x, {
42+
onComplete: (results) => {
43+
completedResults = results
44+
},
45+
})
46+
expect(completedResults).toEqual([])
3347
})
3448

35-
it('continues processing after task error', async () => {
49+
it('continues processing after task error', () => {
3650
const results: number[] = []
37-
const promise = scheduler.processEach([1, 2, 3], (x) => {
51+
scheduler.processEach([1, 2, 3], (x) => {
3852
if (x === 2) throw new Error('fail')
3953
results.push(x)
4054
return x
4155
})
4256
jest.runAllTimers()
43-
await promise
4457
expect(results).toEqual([1, 3])
4558
})
4659
})
4760

4861
describe('priority', () => {
49-
it('processes high priority before normal priority', async () => {
62+
it('processes high priority before normal priority', () => {
5063
const order: string[] = []
5164

5265
scheduler.processEach(['n1', 'n2'], (x) => {
@@ -64,15 +77,13 @@ describe('Scheduler', () => {
6477
)
6578

6679
jest.runAllTimers()
67-
// eslint-disable-next-line compat/compat
68-
await Promise.resolve()
6980

7081
expect(order).toEqual(['h1', 'h2', 'n1', 'n2'])
7182
})
7283
})
7384

7485
describe('yielding', () => {
75-
it('yields to browser after time budget exceeded', async () => {
86+
it('yields to browser after time budget exceeded', () => {
7687
let mockTime = 0
7788
jest.spyOn(performance, 'now').mockImplementation(() => mockTime)
7889
scheduler._reset(30)
@@ -86,16 +97,10 @@ describe('Scheduler', () => {
8697
return x
8798
})
8899

89-
// First tick - should process ~30 items then yield
90100
jest.advanceTimersByTime(0)
91-
// eslint-disable-next-line compat/compat
92-
await Promise.resolve()
93101
expect(results.length).toBeLessThan(100)
94102

95-
// Complete all
96103
jest.runAllTimers()
97-
// eslint-disable-next-line compat/compat
98-
await Promise.resolve()
99104
expect(results).toHaveLength(100)
100105
})
101106
})

packages/browser/src/utils/scheduler.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { isNullish } from '@posthog/core'
33

44
export type Priority = 'high' | 'normal'
55

6+
export interface ProcessEachOptions<R> {
7+
priority?: Priority
8+
onComplete?: (results: R[]) => void
9+
}
10+
611
const DEFAULT_TIME_BUDGET_MS = 30
712

813
class Scheduler {
@@ -11,34 +16,31 @@ class Scheduler {
1116
private _scheduled: ReturnType<typeof setTimeout> | null = null
1217
private _timeBudgetMs = DEFAULT_TIME_BUDGET_MS
1318

14-
processEach<T, R>(items: T[], fn: (item: T, index: number) => R, options?: { priority?: Priority }): Promise<R[]> {
19+
processEach<T, R>(items: T[], fn: (item: T, index: number) => R, options?: ProcessEachOptions<R>): void {
1520
if (items.length === 0) {
16-
// eslint-disable-next-line compat/compat
17-
return Promise.resolve([])
21+
options?.onComplete?.([])
22+
return
1823
}
1924

2025
const results: R[] = new Array(items.length)
2126
let completedCount = 0
2227
const queue = options?.priority === 'high' ? this._highQueue : this._normalQueue
2328

24-
// eslint-disable-next-line compat/compat
25-
return new Promise((resolve) => {
26-
items.forEach((item, index) => {
27-
queue.push(() => {
28-
try {
29-
results[index] = fn(item, index)
30-
} finally {
31-
if (++completedCount === items.length) {
32-
resolve(results)
33-
}
29+
items.forEach((item, index) => {
30+
queue.push(() => {
31+
try {
32+
results[index] = fn(item, index)
33+
} finally {
34+
if (++completedCount === items.length) {
35+
options?.onComplete?.(results)
3436
}
35-
})
37+
}
3638
})
37-
38-
if (isNullish(this._scheduled)) {
39-
this._scheduled = setTimeout(() => this._process(), 0)
40-
}
4139
})
40+
41+
if (isNullish(this._scheduled)) {
42+
this._scheduled = setTimeout(() => this._process(), 0)
43+
}
4244
}
4345

4446
private _process(): void {

0 commit comments

Comments
 (0)