Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: cleanup the code. #4

Merged
merged 2 commits into from
Jan 2, 2024
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
45 changes: 23 additions & 22 deletions src/dwait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import type DeferredPromise from "./deferredPromise";
/**
* List of async methods to let pass through {@link DeferredPromise}
*/
const asyncMethods = ["then", "catch", "finally"];
const ASYNC_METHODS = ["then", "catch", "finally"];

/**
* It is just an empty function to point at
*/
// istanbul ignore next no op code
function noop() {}
function DeferredOperation() {}

/**
* This function will take a `Promise` and will wrap it as a {@link DeferredPromise}
Expand All @@ -33,48 +33,49 @@ function dwaitInternal<T, Y>(
): DeferredPromise<T> {
const task = Promise.resolve(promise);
const result: Box<Promise<T>> = { value: undefined };
return new Proxy<object>(noop, {
const then = (callback?: (target: unknown) => Promise<T>): Promise<T> => {
return task.then((target) => {
result.value = target;
if (callback) {
return callback(target);
} else {
return target;
}
});
};
return new Proxy<object>(DeferredOperation, {
get(_, symbol) {
const prop = symbol as string;
if (asyncMethods.includes(prop)) {
if (ASYNC_METHODS.includes(prop)) {
// @ts-expect-error we are sure that this property exists and is callable.
return (...args: unknown[]) => dwaitInternal(task[prop](...args));
} else if (prop === "await") {
return task.then((target) => {
result.value = target;
return target;
});
return then();
} else if (prop === "toPromise") {
return () =>
task.then((target) => {
result.value = target;
return target;
});
return () => then();
} else {
return dwaitInternal(
task.then((target) => {
result.value = target;
// @ts-expect-error this is just deferred actions of the user, and user has to make sure target property is a valid value
return target[prop];
}),
// @ts-expect-error this is just deferred actions of the user, and user has to make sure target property is a valid value
then((target) => target[prop]),
result
);
}
},
apply(_, thisArg, args) {
return dwaitInternal(
task.then((target) => {
then((target) => {
if (lhs?.value !== undefined) {
// @ts-expect-error this is just deferred actions of the user, and user has to make sure target is a valid function
return Reflect.apply(target, lhs.value, args);
} else {
// @ts-expect-error this is just deferred actions of the user, and user has to make sure target is a valid function
return Reflect.apply(target, thisArg, args);
}
})
}),
result
);
},
}) as DeferredPromise<T & { await: () => Promise<T> }>;
}) as DeferredPromise<T>;
}

/**
Expand All @@ -88,4 +89,4 @@ function dwait<T>(promise: Promise<T> | T): DeferredPromise<T> {
return dwaitInternal(promise);
}

export { dwait };
export default dwait;
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type DeferredPromise from "./deferredPromise";
import { dwait } from "./dwait";
import dwait from "./dwait";

export type { DeferredPromise };
export default dwait;
16 changes: 15 additions & 1 deletion tests/dwait.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { dwait } from "../src/dwait";
import dwait from "../src/dwait";

const OK = "OK";
const ERROR = "ERROR";
Expand Down Expand Up @@ -61,68 +61,82 @@ describe("dwait Tests", () => {
const dwaitPromise = dwait(resolveClass()).baz().bar();
await expect(dwaitPromise.toPromise()).resolves.toEqual(NUMBER);
});

test("should return a DeferredPromise which has a await property containing the native promise of the operation chain", async () => {
const dwaitPromise = dwait(resolveClass()).baz().foo;
await expect(dwaitPromise.await).resolves.toEqual(OKB);
});

test("should return throw on awaiting rejected promises", async () => {
const dwaitPromise = dwait(rejectMock());
await expect(dwaitPromise).rejects.toEqual(ERROR);
});

test("should return a DeferredPromise<string> which contains split function acting like the native string", async () => {
const dwaitPromise = dwait(resolveClass()).foo;
await expect(dwaitPromise.split("K").await).resolves.toEqual(
OKA.split("K")
);
});

test("should return a DeferredPromise<string> which contains match function acting like the native string", async () => {
const dwaitPromise = dwait(resolveClass()).foo;
await expect(dwaitPromise.match(OK)?.await).resolves.toEqual(OKA.match(OK));
});

test("should return a DeferredPromise<string> which contains matchAll function acting like the native string", async () => {
const matchResult = await dwait(resolveClass()).foo.matchAll(MATCH_OK)
?.await;
expect(Array.from(matchResult)).toEqual(Array.from(OKA.matchAll(MATCH_OK)));
});

test("should return a DeferredPromise<string> which contains replace function acting like the native string", async () => {
const dwaitPromise = dwait(resolveClass()).foo;
await expect(
dwaitPromise.replace(OK, NUMBER.toString())?.await
).resolves.toEqual(OKA.replace(OK, NUMBER.toString()));
});

test("should return a DeferredPromise<string> which contains search function acting like the native string", async () => {
const dwaitPromise = dwait(resolveClass()).foo;
await expect(dwaitPromise.search("K").await).resolves.toEqual(
OKA.search("K")
);
});

test("should return a DeferredPromise<object> which contains toString function acting like the native object", async () => {
const dwaitPromise = dwait(resolveClass()).baz();
await expect(dwaitPromise.toString()).resolves.toEqual(classA.toString());
});

test("should return a DeferredPromise which isn't callable", async () => {
const dwaitPromise = dwait(resolveClass());
// @ts-expect-error it isn't callabe
await expect(dwaitPromise()).rejects.toThrow();
});

test("should provide a promise with the exact same result as native version", async () => {
const dwaitPromise = dwait(resolveMock()).toPromise();
await expect(dwaitPromise).resolves.toBe(OK);
});

test("should be able to wrap a constructor successfully", async () => {
const dwaitPromise = dwait(new TestClass(OK, NUMBER, OK));
await expect(dwaitPromise.await).resolves.toEqual(
expect.objectContaining({ foo: OK + OK })
);
});

test("should be able to wrap functions with returning promises", async () => {
const dwaitPromise = dwait(new TestClass(OK, NUMBER, OK)).addBar();
await expect(dwaitPromise.await).resolves.toEqual(NUMBER + 1);
});

test("should be able to wrap a function and use it as a deferred function", async () => {
const dwaitFunc = dwait(resolveMock);
await expect(dwaitFunc().await).resolves.toEqual(OK);
});

test("should be able to wrap cycling dependencies", async () => {
let dwaitPromise = dwait(resolveClass());
for (let i = 0, len = 999; i < len; ++i) {
Expand Down