Skip to content

Commit 92aa121

Browse files
committed
extract safeUnref
1 parent dfa682b commit 92aa121

File tree

4 files changed

+51
-12
lines changed

4 files changed

+51
-12
lines changed

packages/core/src/client.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import { safeMathRandom } from './utils/randomSafeContext';
4949
import { reparentChildSpans, shouldIgnoreSpan } from './utils/should-ignore-span';
5050
import { showSpanDropWarning } from './utils/spanUtils';
5151
import { rejectedSyncPromise } from './utils/syncpromise';
52+
import { safeUnref } from './utils/timer';
5253
import { convertSpanJsonToTransactionEvent, convertTransactionEventToSpanJson } from './utils/transactionEvent';
5354

5455
const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured.";
@@ -1155,12 +1156,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
11551156
let ticked = 0;
11561157

11571158
while (!timeout || ticked < timeout) {
1158-
await new Promise(resolve => {
1159-
const timer = setTimeout(resolve, 1);
1160-
if (typeof timer !== 'number' && timer.unref) {
1161-
timer.unref();
1162-
}
1163-
});
1159+
await new Promise(resolve => safeUnref(setTimeout(resolve, 1)));
11641160

11651161
if (!this._numProcessing) {
11661162
return true;

packages/core/src/utils/promisebuffer.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { rejectedSyncPromise, resolvedSyncPromise } from './syncpromise';
2+
import { safeUnref } from './timer';
23

34
export interface PromiseBuffer<T> {
45
// exposes the internal array so tests can assert on the state of it.
@@ -79,12 +80,7 @@ export function makePromiseBuffer<T>(limit: number = 100): PromiseBuffer<T> {
7980

8081
const promises = [
8182
drainPromise,
82-
new Promise<boolean>(resolve => {
83-
const timer = setTimeout(() => resolve(false), timeout);
84-
if (typeof timer !== 'number' && timer.unref) {
85-
timer.unref();
86-
}
87-
}),
83+
new Promise<boolean>(resolve => safeUnref(setTimeout(() => resolve(false), timeout))),
8884
];
8985

9086
return Promise.race(promises);

packages/core/src/utils/timer.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Calls `unref` on a timer, if the method is available on @param timer.
3+
*
4+
* `unred()` is used to allow processes to exit immediately, even if the timer
5+
* is still running and hasn't resolved yet.
6+
*
7+
* Use this in places where code can run on browser or server, since browsers
8+
* do not support `unref`.
9+
*/
10+
export function safeUnref(timer: ReturnType<typeof setTimeout>): ReturnType<typeof setTimeout> {
11+
if (typeof timer === 'object' && typeof timer.unref === 'function') {
12+
timer.unref();
13+
}
14+
return timer;
15+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, expect, it, vi } from 'vitest';
2+
import { safeUnref } from '../../../src/utils/timer';
3+
4+
describe('safeUnref', () => {
5+
it('calls unref on a NodeJS timer', () => {
6+
const timeout = setTimeout(() => {}, 1000);
7+
const unrefSpy = vi.spyOn(timeout, 'unref');
8+
safeUnref(timeout);
9+
expect(unrefSpy).toHaveBeenCalledOnce();
10+
});
11+
12+
it('returns the timer', () => {
13+
const timeout = setTimeout(() => {}, 1000);
14+
const result = safeUnref(timeout);
15+
expect(result).toBe(timeout);
16+
});
17+
18+
it('handles multiple unref calls', () => {
19+
const timeout = setTimeout(() => {}, 1000);
20+
const unrefSpy = vi.spyOn(timeout, 'unref');
21+
22+
const result = safeUnref(timeout);
23+
result.unref();
24+
25+
expect(unrefSpy).toHaveBeenCalledTimes(2);
26+
});
27+
28+
it("doesn't throw for a browser timer", () => {
29+
const timer = safeUnref(385 as unknown as ReturnType<typeof setTimeout>);
30+
expect(timer).toBe(385);
31+
});
32+
});

0 commit comments

Comments
 (0)