Skip to content

Commit

Permalink
Use async fn's .catch to avoid unhandled rejection
Browse files Browse the repository at this point in the history
Add explicit `isPending` value instead of overloading role of `status`. Could probably do without it, but it makes the intent more clear.
  • Loading branch information
John Schulz committed Aug 19, 2020
1 parent deafa13 commit a913911
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 22 deletions.
20 changes: 10 additions & 10 deletions x-pack/plugins/ingest_manager/server/services/setup_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ async function sleep(ms: number) {

describe('awaitIfPending', () => {
it('first promise called blocks others', async () => {
const fnA = jest.fn();
const fnB = jest.fn();
const fnC = jest.fn();
const fnD = jest.fn();
const fnA = jest.fn().mockImplementation(async () => {});
const fnB = jest.fn().mockImplementation(async () => {});
const fnC = jest.fn().mockImplementation(async () => {});
const fnD = jest.fn().mockImplementation(async () => {});
const promises = [
awaitIfPending(fnA),
awaitIfPending(fnB),
Expand Down Expand Up @@ -92,12 +92,12 @@ describe('awaitIfPending', () => {
it('does not block other calls after batch is fulfilled. can call again for a new result', async () => {
const fnA = jest
.fn()
.mockImplementationOnce(() => 'fnA first')
.mockImplementationOnce(() => 'fnA second')
.mockImplementation(() => 'fnA default/2+');
const fnB = jest.fn();
const fnC = jest.fn();
const fnD = jest.fn();
.mockImplementationOnce(async () => 'fnA first')
.mockImplementationOnce(async () => 'fnA second')
.mockImplementation(async () => 'fnA default/2+');
const fnB = jest.fn().mockImplementation(async () => {});
const fnC = jest.fn().mockImplementation(async () => {});
const fnD = jest.fn().mockImplementation(async () => {});
let promises = [
awaitIfPending(fnA),
awaitIfPending(fnB),
Expand Down
21 changes: 9 additions & 12 deletions x-pack/plugins/ingest_manager/server/services/setup_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,33 @@
*/

// the promise which tracks the setup
let status: Promise<unknown> | undefined;
let status: Promise<any> | undefined;
let isPending = false;
// default resolve to guard against "undefined is not a function" errors
let onResolve = (value?: unknown) => {};
let onReject = (reason: any) => {};

export async function awaitIfPending(asyncFunction: Function) {
export async function awaitIfPending<T>(asyncFunction: Function): Promise<T> {
// pending successful or failed attempt
if (status) {
if (isPending) {
// don't run concurrent installs
// return a promise which will eventually resolve/reject
return status;
} else {
// create the initial promise
status = new Promise((res, rej) => {
isPending = true;
onResolve = res;
onReject = rej;
});
}
try {
// if everything works, mark the tracking promise as resolved
const result = await asyncFunction();
const result = await asyncFunction().catch(onReject);
onResolve(result);
// * reset the tracking promise to try again next time
status = undefined;
return result;
} catch (error) {
// if something fails
onReject(error);
// * reset the tracking promise to try again next time
status = undefined;
// * return the rejection so it can be dealt with now
return Promise.reject(error);
}
isPending = false;
return status;
}

0 comments on commit a913911

Please sign in to comment.