Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions packages/property-provider/src/memoize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,28 @@ describe("memoize", () => {
const memoized = memoize(provider);
expect(provider).toHaveBeenCalledTimes(0);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const index in [...Array(repeatTimes).keys()]) {
for (const _ in [...Array(repeatTimes).keys()]) {
expect(await memoized()).toStrictEqual(mockReturn);
expect(provider).toHaveBeenCalledTimes(1);
}
});

it("should always return the same promise", () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unit test is removed. We cannot return the same promise because the memoize() now needs to await the provider to resolve(). The unit test was introduced in #56 but with no explicit purpose. I have run the integration test to make sure it feature doesn't affect the SDK now.

expect.assertions(repeatTimes * 2);

it("should retry provider if previous provider is failed", async () => {
provider
.mockReset()
.mockRejectedValueOnce("Error")
.mockResolvedValueOnce("Retry")
.mockRejectedValueOnce("Should not call 3rd time");
const memoized = memoize(provider);
const result = memoized();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const index in [...Array(repeatTimes).keys()]) {
expect(memoized()).toStrictEqual(result);
expect(provider).toHaveBeenCalledTimes(1);
try {
await memoized();
fail();
} catch (e) {
expect(e).toBe("Error");
}
expect(await memoized()).toBe("Retry");
expect(await memoized()).toBe("Retry");
expect(provider).toBeCalledTimes(2);
});
});

Expand Down Expand Up @@ -124,5 +130,24 @@ describe("memoize", () => {
return requiresRefreshFalseTest();
});
});

it("should retry provider if previous provider is failed", async () => {
provider
.mockReset()
.mockRejectedValueOnce("Error")
.mockResolvedValueOnce("Retry")
.mockRejectedValueOnce("Should not call 3rd time");
isExpired.mockReset().mockReturnValue(false);
const memoized = memoize(provider, isExpired);
try {
await memoized();
fail();
} catch (e) {
expect(e).toBe("Error");
}
expect(await memoized()).toBe("Retry");
expect(await memoized()).toBe("Retry");
expect(provider).toBeCalledTimes(2);
});
});
});
15 changes: 7 additions & 8 deletions packages/property-provider/src/memoize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,36 @@ export const memoize: MemoizeOverload = <T>(
isExpired?: (resolved: T) => boolean,
requiresRefresh?: (resolved: T) => boolean
): Provider<T> => {
let result: any;
let resolved: any;
let hasResult: boolean;
if (isExpired === undefined) {
// This is a static memoization; no need to incorporate refreshing
return () => {
return async () => {
if (!hasResult) {
result = provider();
resolved = await provider();
hasResult = true;
}
return result;
return resolved;
};
}

let isConstant = false;

return async () => {
if (!hasResult) {
result = provider();
resolved = await provider();
hasResult = true;
}
if (isConstant) {
return result;
return resolved;
}

const resolved = await result;
if (requiresRefresh && !requiresRefresh(resolved)) {
isConstant = true;
return resolved;
}
if (isExpired(resolved)) {
return (result = provider());
return (resolved = await provider());
}
return resolved;
};
Expand Down