Skip to content

Commit 16515df

Browse files
authored
fix: Better handle waiting for initialization for failure cases. (#314)
Affects #312
1 parent f45910f commit 16515df

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

packages/shared/sdk-server/__tests__/LDClientImpl.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ describe('LDClientImpl', () => {
5151
expect(callbacks.onError).not.toBeCalled();
5252
});
5353

54+
it('wait for initialization completes even if initialization completes before it is called', (done) => {
55+
setupMockStreamingProcessor();
56+
client = createClient();
57+
58+
setTimeout(async () => {
59+
const initializedClient = await client.waitForInitialization();
60+
expect(initializedClient).toEqual(client);
61+
done();
62+
}, 10);
63+
});
64+
65+
it('waiting for initialization the second time produces the same result', async () => {
66+
client = createClient();
67+
await client.waitForInitialization();
68+
69+
const initializedClient = await client.waitForInitialization();
70+
expect(initializedClient).toEqual(client);
71+
});
72+
5473
it('fires ready event in offline mode', async () => {
5574
client = createClient({ offline: true });
5675
const initializedClient = await client.waitForInitialization();
@@ -74,6 +93,29 @@ describe('LDClientImpl', () => {
7493
expect(callbacks.onError).toBeCalled();
7594
});
7695

96+
it('initialization promise is rejected even if the failure happens before wait is called', (done) => {
97+
setupMockStreamingProcessor(true);
98+
client = createClient();
99+
100+
setTimeout(async () => {
101+
await expect(client.waitForInitialization()).rejects.toThrow('failed');
102+
103+
expect(client.initialized()).toBeFalsy();
104+
expect(callbacks.onReady).not.toBeCalled();
105+
expect(callbacks.onFailed).toBeCalled();
106+
expect(callbacks.onError).toBeCalled();
107+
done();
108+
}, 10);
109+
});
110+
111+
it('waiting a second time results in the same rejection', async () => {
112+
setupMockStreamingProcessor(true);
113+
client = createClient();
114+
115+
await expect(client.waitForInitialization()).rejects.toThrow('failed');
116+
await expect(client.waitForInitialization()).rejects.toThrow('failed');
117+
});
118+
77119
it('isOffline returns true in offline mode', () => {
78120
client = createClient({ offline: true });
79121
expect(client.isOffline()).toEqual(true);

packages/shared/sdk-server/src/LDClientImpl.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export default class LDClientImpl implements LDClient {
9090

9191
private initReject?: (err: Error) => void;
9292

93+
private rejectionReason: Error | undefined;
94+
9395
private initializedPromise?: Promise<LDClient>;
9496

9597
private logger?: LDLogger;
@@ -231,9 +233,30 @@ export default class LDClientImpl implements LDClient {
231233
}
232234

233235
waitForInitialization(): Promise<LDClient> {
236+
// An initialization promise is only created if someone is going to use that promise.
237+
// If we always created an initialization promise, and there was no call waitForInitialization
238+
// by the time the promise was rejected, then that would result in an unhandled promise
239+
// rejection.
240+
241+
// Initialization promise was created by a previous call to waitForInitialization.
242+
if (this.initializedPromise) {
243+
return this.initializedPromise;
244+
}
245+
246+
// Initialization completed before waitForInitialization was called, so we have completed
247+
// and there was no promise. So we make a resolved promise and return it.
234248
if (this.initState === InitState.Initialized) {
235-
return Promise.resolve(this);
249+
this.initializedPromise = Promise.resolve(this);
250+
return this.initializedPromise;
236251
}
252+
253+
// Initialization failed before waitForInitialization was called, so we have completed
254+
// and there was no promise. So we make a rejected promise and return it.
255+
if (this.initState === InitState.Failed) {
256+
this.initializedPromise = Promise.reject(this.rejectionReason);
257+
return this.initializedPromise;
258+
}
259+
237260
if (!this.initializedPromise) {
238261
this.initializedPromise = new Promise((resolve, reject) => {
239262
this.initResolve = resolve;
@@ -703,6 +726,7 @@ export default class LDClientImpl implements LDClient {
703726

704727
if (!this.initialized()) {
705728
this.initState = InitState.Failed;
729+
this.rejectionReason = error;
706730
this.initReject?.(error);
707731
}
708732
}

0 commit comments

Comments
 (0)