Skip to content

Commit 5354bf2

Browse files
committed
fix sync promise stuff
1 parent a012502 commit 5354bf2

File tree

3 files changed

+297
-22
lines changed

3 files changed

+297
-22
lines changed

packages/core/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ export {
247247
SyncPromise,
248248
rejectedSyncPromise,
249249
resolvedSyncPromise,
250-
makeSyncPromise,
251250
} from './utils/syncpromise';
252251
export { browserPerformanceTimeOrigin, dateTimestampInSeconds, timestampInSeconds } from './utils/time';
253252
export {

packages/core/src/utils/syncpromise.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,6 @@ type Executor<T> = (resolve: (value?: T | PromiseLike<T> | null) => void, reject
1111
type PromiseTry = <T>(executor: Executor<T>) => PromiseLike<T>;
1212
type PromiseWithTry = PromiseConstructor & { try: PromiseTry };
1313

14-
/**
15-
* Takes an executor and returns a promise that is executed synchronously (if the executor is synchronous) or else asynchronously.
16-
* It always returns a promise.
17-
*
18-
* This uses the native Promise.try, if it exists, else our SyncPromise implementation.
19-
*/
20-
export function makeSyncPromise<T>(executor: Executor<T>): PromiseLike<T> {
21-
if (hasPromiseTry(Promise)) {
22-
return Promise.try(executor);
23-
}
24-
25-
// eslint-disable-next-line deprecation/deprecation
26-
return new SyncPromise(executor);
27-
}
28-
2914
function hasPromiseTry(Promise: typeof globalThis.Promise): Promise is PromiseWithTry {
3015
return 'try' in Promise && typeof Promise.try === 'function';
3116
}
@@ -41,7 +26,8 @@ export function resolvedSyncPromise<T>(value: T | PromiseLike<T>): PromiseLike<T
4126
* @returns the resolved sync promise
4227
*/
4328
export function resolvedSyncPromise<T>(value?: T | PromiseLike<T>): PromiseLike<T> {
44-
return makeSyncPromise(() => value);
29+
// eslint-disable-next-line deprecation/deprecation
30+
return hasPromiseTry(Promise) ? Promise.try(() => value) : new SyncPromise(resolve => resolve(value));
4531
}
4632

4733
/**
@@ -51,16 +37,19 @@ export function resolvedSyncPromise<T>(value?: T | PromiseLike<T>): PromiseLike<
5137
* @returns the rejected sync promise
5238
*/
5339
export function rejectedSyncPromise<T = never>(reason?: any): PromiseLike<T> {
54-
return makeSyncPromise(() => {
55-
throw reason;
56-
});
40+
return hasPromiseTry(Promise)
41+
? Promise.try(() => {
42+
throw reason;
43+
})
44+
: // eslint-disable-next-line deprecation/deprecation
45+
new SyncPromise((_, reject) => reject(reason));
5746
}
5847

5948
/**
6049
* Thenable class that behaves like a Promise and follows it's interface
6150
* but is not async internally
6251
*
63-
* @deprecated Use makeSyncPromise instead.
52+
* @deprecated This export will be removed in a future version.
6453
*/
6554
export class SyncPromise<T> implements PromiseLike<T> {
6655
private _state: State;

packages/core/test/lib/utils/syncpromise.test.ts

Lines changed: 288 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
/* eslint-disable deprecation/deprecation */
2-
import { describe, expect, test, vi } from 'vitest';
2+
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest';
33
import { rejectedSyncPromise, resolvedSyncPromise, SyncPromise } from '../../../src/utils/syncpromise';
44

55
describe('SyncPromise', () => {
6+
// @ts-expect-error We want to ensure to test this without the native Promise.try
7+
const promiseTry = Promise.try;
8+
9+
beforeAll(() => {
10+
// @ts-expect-error We want to ensure to test this without the native Promise.try
11+
Promise.try = undefined;
12+
});
13+
14+
afterAll(() => {
15+
// @ts-expect-error Restore this
16+
Promise.try = promiseTry;
17+
});
18+
619
test('simple', async () => {
720
expect.assertions(1);
821

@@ -13,6 +26,35 @@ describe('SyncPromise', () => {
1326
});
1427
});
1528

29+
test('simple error catching', async () => {
30+
expect.assertions(1);
31+
32+
const error = new Error('test error');
33+
34+
return new SyncPromise<number>(() => {
35+
throw error;
36+
}).catch(val => {
37+
expect(val).toBe(error);
38+
});
39+
});
40+
41+
test('simple error in then', async () => {
42+
expect.assertions(1);
43+
44+
const error = new Error('test error');
45+
46+
return new SyncPromise<number>(() => {
47+
throw error;
48+
}).then(
49+
() => {
50+
throw new Error('THIS SHOULD NOT BE CALLED!');
51+
},
52+
val => {
53+
expect(val).toBe(error);
54+
},
55+
);
56+
});
57+
1658
test('simple chaining', async () => {
1759
expect.assertions(1);
1860

@@ -99,6 +141,9 @@ describe('SyncPromise', () => {
99141
expect.assertions(1);
100142

101143
const p = resolvedSyncPromise(10);
144+
145+
console.log(p);
146+
102147
return p.then(val => {
103148
expect(val).toBe(10);
104149
});
@@ -267,3 +312,245 @@ describe('SyncPromise', () => {
267312
});
268313
});
269314
});
315+
316+
describe('resolvedSyncPromise', () => {
317+
test('simple chaining', async () => {
318+
expect.assertions(1);
319+
320+
return new SyncPromise<number>(resolve => {
321+
resolve(42);
322+
})
323+
.then(_ => resolvedSyncPromise('a'))
324+
.then(_ => resolvedSyncPromise(0.1))
325+
.then(_ => resolvedSyncPromise(false))
326+
.then(val => {
327+
expect(val).toBe(false);
328+
});
329+
});
330+
331+
test('compare to regular promise', async () => {
332+
expect.assertions(2);
333+
334+
const ap = new Promise<string>(resolve => {
335+
resolve('1');
336+
});
337+
338+
const bp = new Promise<string>(resolve => {
339+
resolve('2');
340+
});
341+
342+
const cp = new Promise<string>(resolve => {
343+
resolve('3');
344+
});
345+
346+
const fp = async (s: PromiseLike<string>, prepend: string) =>
347+
new Promise<string>(resolve => {
348+
void s
349+
.then(val => {
350+
resolve(prepend + val);
351+
})
352+
.then(null, _ => {
353+
// bla
354+
});
355+
});
356+
357+
const res = await cp
358+
.then(async val => fp(Promise.resolve('x'), val))
359+
.then(async val => fp(bp, val))
360+
.then(async val => fp(ap, val));
361+
362+
expect(res).toBe('3x21');
363+
364+
const a = new SyncPromise<string>(resolve => {
365+
resolve('1');
366+
});
367+
368+
const b = new SyncPromise<string>(resolve => {
369+
resolve('2');
370+
});
371+
372+
const c = new SyncPromise<string>(resolve => {
373+
resolve('3');
374+
});
375+
376+
const f = (s: SyncPromise<string>, prepend: string) =>
377+
new SyncPromise<string>(resolve => {
378+
void s
379+
.then(val => {
380+
resolve(prepend + val);
381+
})
382+
.then(null, () => {
383+
// no-empty
384+
});
385+
});
386+
387+
return (
388+
c
389+
// @ts-expect-error Argument of type 'PromiseLike<string>' is not assignable to parameter of type 'SyncPromise<string>'
390+
.then(val => f(resolvedSyncPromise('x'), val))
391+
.then(val => f(b, val))
392+
.then(val => f(a, val))
393+
.then(val => {
394+
expect(val).toBe(res);
395+
})
396+
);
397+
});
398+
399+
test('simple static', async () => {
400+
expect.assertions(1);
401+
402+
const p = resolvedSyncPromise(10);
403+
404+
return p.then(val => {
405+
expect(val).toBe(10);
406+
});
407+
});
408+
409+
test('calling the callback immediately', () => {
410+
expect.assertions(1);
411+
412+
let foo: number = 1;
413+
414+
new SyncPromise<number>(_ => {
415+
foo = 2;
416+
});
417+
418+
expect(foo).toEqual(2);
419+
});
420+
421+
test('calling the callback not immediately', () => {
422+
vi.useFakeTimers();
423+
expect.assertions(4);
424+
425+
const qp = new SyncPromise<number>(resolve =>
426+
setTimeout(() => {
427+
resolve(2);
428+
}),
429+
);
430+
void qp
431+
.then(value => {
432+
expect(value).toEqual(2);
433+
})
434+
.then(null, () => {
435+
// no-empty
436+
});
437+
expect(qp).not.toHaveProperty('_value');
438+
void qp
439+
.then(value => {
440+
expect(value).toEqual(2);
441+
})
442+
.then(null, () => {
443+
// no-empty
444+
});
445+
vi.runAllTimers();
446+
expect(qp).toHaveProperty('_value');
447+
});
448+
449+
test('multiple then returning undefined', async () => {
450+
expect.assertions(3);
451+
452+
return resolvedSyncPromise(2)
453+
.then(result => {
454+
expect(result).toEqual(2);
455+
})
456+
.then(result => {
457+
expect(result).toBeUndefined();
458+
})
459+
.then(result => {
460+
expect(result).toBeUndefined();
461+
});
462+
});
463+
464+
test('multiple then returning different values', async () => {
465+
expect.assertions(3);
466+
467+
return resolvedSyncPromise(2)
468+
.then(result => {
469+
expect(result).toEqual(2);
470+
return 3;
471+
})
472+
.then(result => {
473+
expect(result).toEqual(3);
474+
return 4;
475+
})
476+
.then(result => {
477+
expect(result).toEqual(4);
478+
});
479+
});
480+
481+
test('multiple then returning different SyncPromise', async () => {
482+
expect.assertions(2);
483+
484+
return resolvedSyncPromise(2)
485+
.then(result => {
486+
expect(result).toEqual(2);
487+
return resolvedSyncPromise('yo');
488+
})
489+
.then(result => {
490+
expect(result).toEqual('yo');
491+
});
492+
});
493+
});
494+
495+
describe('rejectedSyncPromise', () => {
496+
test('simple', async () => {
497+
expect.assertions(1);
498+
499+
return new SyncPromise<number>(resolve => {
500+
resolve(42);
501+
}).then(val => {
502+
expect(val).toBe(42);
503+
});
504+
});
505+
506+
test('simple error catching', async () => {
507+
expect.assertions(1);
508+
509+
const error = new Error('test error');
510+
511+
return rejectedSyncPromise(error).then(
512+
() => {
513+
throw new Error('THIS SHOULD NOT BE CALLED!');
514+
},
515+
val => {
516+
expect(val).toBe(error);
517+
},
518+
);
519+
});
520+
test('reject immediately and do not call then', async () => {
521+
expect.assertions(1);
522+
523+
return new SyncPromise<number>((_, reject) => {
524+
reject('test');
525+
})
526+
.then(_ => {
527+
expect(true).toBeFalsy();
528+
})
529+
.then(null, reason => {
530+
expect(reason).toBe('test');
531+
});
532+
});
533+
534+
test('reject', async () => {
535+
expect.assertions(1);
536+
537+
return new SyncPromise<number>((_, reject) => {
538+
reject('test');
539+
}).then(null, reason => {
540+
expect(reason).toBe('test');
541+
});
542+
});
543+
544+
test('rejecting after first then', async () => {
545+
expect.assertions(2);
546+
547+
return resolvedSyncPromise(2)
548+
.then(value => {
549+
expect(value).toEqual(2);
550+
return rejectedSyncPromise('wat');
551+
})
552+
.then(null, reason => {
553+
expect(reason).toBe('wat');
554+
});
555+
});
556+
});

0 commit comments

Comments
 (0)