Skip to content

Instantly share code, notes, and snippets.

@ryanguill
Created October 19, 2024 21:43
Show Gist options
  • Save ryanguill/e89931f9d223e74dabcb070879a58298 to your computer and use it in GitHub Desktop.
Save ryanguill/e89931f9d223e74dabcb070879a58298 to your computer and use it in GitHub Desktop.
/**
* Runs multiple queries in parallel and resolves with the first query that returns records.
* If no query returns records, it rejects with an error.
*
* @param queries An array of promises representing database queries.
* @returns A promise that resolves with the first valid result or rejects if no results are found.
*/
export async function firstQueryWithResultsWithKey<T>({
queriesAsRecord,
resultTest = ({ result }) => Array.isArray(result) && result.length > 0,
noResultsReturn = null,
}: {
queriesAsRecord: Record<string, Promise<T>>;
resultTest?: ({ result, key }: { result: T; key: string | null }) => boolean;
noResultsReturn?: T | null;
}): Promise<{ result: T; key: string | null }> {
return new Promise((resolve, reject) => {
let completed = false; // Track if a query has returned results
let pendingCount = Object.keys(queriesAsRecord).length; // Track the number of pending queries
if (pendingCount === 0) {
if (noResultsReturn) {
resolve({ result: noResultsReturn, key: null });
} else {
reject(new Error("No queries returned any records"));
}
}
// Define a function to handle each query independently
const handleQuery = async (query: Promise<T>, key: string) => {
try {
if (!completed) {
const result = await query;
// If the result has records and no other query has resolved
if (resultTest({ result, key }) && !completed) {
completed = true; // Mark as completed
resolve({ result, key }); // Resolve with the first valid result
}
}
} catch (error) {
console.error("Query error:", error); // Log query errors
} finally {
// Decrement pending count and check if all queries are exhausted
pendingCount--;
if (pendingCount === 0 && !completed) {
if (noResultsReturn) {
resolve({ result: noResultsReturn, key: null });
} else {
reject(new Error("No queries returned any records"));
}
}
}
};
// Start all queries in parallel
Object.entries(queriesAsRecord).forEach(([key, query]) =>
handleQuery(query, key),
);
});
}
export async function firstQueryWithResults<T>({
queries,
resultTest = (result) => Array.isArray(result) && result.length > 0,
noResultsReturn = null,
}: {
queries: Promise<T>[];
resultTest?: (result: T) => boolean;
noResultsReturn?: T | null;
}): Promise<T> {
let queriesAsRecord = Object.fromEntries(
queries.map((query, index) => [index.toString(), query]),
);
const { result } = await firstQueryWithResultsWithKey({
queriesAsRecord,
resultTest: ({ result }) => resultTest(result),
noResultsReturn,
});
return result;
}
describe("firstQueryWithResults", () => {
type QueryResult = any[];
const wait = (interval: number) =>
new Promise((resolve) => setTimeout(resolve, interval));
it("should handle a default resultTest", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [{ a: id }];
};
// Call the function with an array of query promises
let error: Error | null = null;
let result: QueryResult | null = null;
try {
result = await firstQueryWithResults({
queries: [
query1({ id: "1" }),
query2({ id: "2" }),
query3({ id: "3" }),
],
});
} catch (err) {
error = err as Error;
}
expect(error).toBeNull();
expect(result).toEqual([{ a: "3" }]);
});
it("should return the first result from the queries 1", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [{ a: id }];
};
// Call the function with an array of query promises
let error: Error | null = null;
let result: QueryResult | null = null;
try {
result = await firstQueryWithResults({
queries: [
query1({ id: "1" }),
query2({ id: "2" }),
query3({ id: "3" }),
],
resultTest: (result) => result.length > 0,
});
} catch (err) {
error = err as Error;
}
expect(error).toBeNull();
expect(result).toEqual([{ a: "3" }]);
});
it("should return the first result from the queries 2", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [{ a: id }];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [];
};
// Call the function with an array of query promises
let error: Error | null = null;
let result: QueryResult | null = null;
try {
result = await firstQueryWithResults({
queries: [
query1({ id: "1" }),
query2({ id: "2" }),
query3({ id: "3" }),
],
resultTest: (result) => result.length > 0,
});
} catch (err) {
error = err as Error;
}
expect(error).toBeNull();
expect(result).toEqual([{ a: "1" }]);
});
it("should return the first result from the queries 3", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [{ a: id }];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [];
};
// Call the function with an array of query promises
let error: Error | null = null;
let result: QueryResult | null = null;
try {
result = await firstQueryWithResults({
queries: [
query1({ id: "1" }),
query2({ id: "2" }),
query3({ id: "3" }),
],
resultTest: (result) => result.length > 0,
});
} catch (err) {
error = err as Error;
}
expect(error).toBeNull();
expect(result).toEqual([{ a: "2" }]);
});
it("should return an error if no queries return results", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [];
};
// Call the function with an array of query promises
let error: Error | null = null;
let result: QueryResult | null = null;
try {
result = await firstQueryWithResults({
queries: [
query1({ id: "1" }),
query2({ id: "2" }),
query3({ id: "3" }),
],
resultTest: (result) => result.length > 0,
});
} catch (err) {
error = err as Error;
}
expect(error).toBeInstanceOf(Error);
expect(result).toBeNull();
});
it("should return the noResultsReturn value if no queries return results", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [];
};
let result: QueryResult | null = null;
result = await firstQueryWithResults({
queries: [query1({ id: "1" }), query2({ id: "2" }), query3({ id: "3" })],
resultTest: (result) => result.length > 0,
noResultsReturn: [],
});
expect(result).toEqual([]);
});
it("should handle no queries", async () => {
// Call the function with an array of query promises
let error: Error | null = null;
let result: QueryResult | null = null;
try {
result = await firstQueryWithResults({
queries: [],
resultTest: (result) => result.length > 0,
noResultsReturn: [],
});
} catch (err) {
error = err as Error;
}
expect(error).toBeNull();
expect(result).toEqual([]);
});
it("should return the first result from the queries with key", async () => {
const query1 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(100);
return [];
};
const query2 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(200);
return [];
};
const query3 = async ({ id }: { id: string }): Promise<QueryResult> => {
await wait(300);
return [{ a: id }];
};
// Call the function with an array of query promises
let error: Error | null = null;
let result: { result: QueryResult; key: string | null } | null = null;
try {
result = await firstQueryWithResultsWithKey({
queriesAsRecord: {
query1: query1({ id: "1" }),
query2: query2({ id: "2" }),
query3: query3({ id: "3" }),
},
resultTest: ({ result }) => result.length > 0,
});
} catch (err) {
error = err as Error;
}
expect(error).toBeNull();
expect(result).toEqual({ result: [{ a: "3" }], key: "query3" });
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment