Last active
November 14, 2023 21:15
-
-
Save Justin-Credible/1adc7dd5f1c06964771258e0c980e3da to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Normally, when using the await keyword to wait on a promise, if the promise is | |
* rejected then an exception will be thrown. This means that to handle a rejection | |
* you would need to try/catch all your await usages, which is inconvenient. | |
* | |
* This helper is meant to be used with the await keyword to wrap the promise | |
* so that it never throws an exception; it is guaranteed to resolve successfully. | |
* | |
* In the case of an error, it will be available in the resolved result object so | |
* that the caller can handle the error without a try/catch. | |
* | |
* For example, this: | |
* | |
* let data = null; | |
* try { | |
* data = await this.DataSource.retrieve(); | |
* } catch (error) { ... } | |
* | |
* becomes this: | |
* | |
* let result = await doTry(this.DataSource.retrieve()) | |
* if (err) { ... } | |
* let data = result.data; | |
* | |
* If you want added protection against uncaught exceptions, wrap with a function: | |
* | |
* let result = await doTry(() => this.DataSource.retrieve()) | |
* if (err) { ... } | |
* let data = result.data; | |
* | |
* @param promise The promise to wait on. | |
* @returns A promise that ALWAYS resolves succesfully. | |
*/ | |
export function doTry<T>(promiseOrFactory: Promise<T> | PromiseFactory<T>): Promise<DoTryResult<T>> { | |
let promise: Promise<T> = null; | |
let factoryFunctionError: any; | |
if (typeof(promiseOrFactory) === "function") { | |
// If a factory function was passed, invoke it to obtain the promise. | |
try { | |
let factory = <PromiseFactory<T>> promiseOrFactory; | |
promise = factory(); | |
} | |
catch (error) { | |
// If the function threw an exception without returning a promise, then | |
// save off the error so we can resolve with it below. | |
factoryFunctionError = error; | |
} | |
} | |
else { | |
// If a promise was provided instead of a factory, use it. | |
// The official Promise types conflict with Angular's types. For our purposes | |
// it doesn't matter, we just need a .then(reject, resolve) method that we can | |
// invoke down below. So we'll perform a cast to this generic type here. | |
promise = <Promise<T>> promiseOrFactory; | |
} | |
// Create a promise wrapper and listen for the then/catch on the original promise. | |
// Note that we resolve successfully in both cases so a rejection will never occur. | |
return new Promise<DoTryResult<T>>((resolve, reject) => { | |
// If we don't have a promise at this point, either the factory function threw | |
// an exception or the caller passed a null/undefined value into the doTry() helper. | |
if (!promise) { | |
let error = factoryFunctionError ? factoryFunctionError : new Error("AsyncAwaitHelper.doTry(): No promise was provided."); | |
resolve({ | |
error: error, | |
data: null, | |
}); | |
return; | |
} | |
// If we did receive a promise, then wait for a success or rejection, and then | |
// resolve the wrapped promise with the custom result interface structure. | |
promise.then((data: T) => { | |
resolve({ | |
error: null, | |
data: data, | |
}); | |
}, (error: any) => { | |
resolve({ | |
error: error, | |
data: null, | |
}); | |
}); | |
}); | |
} | |
/** | |
* A factory function that returns a promise. | |
*/ | |
export type PromiseFactory<T> = () => Promise<T>; | |
/** | |
* The result of the doTry() helper method for either a success or failure case. | |
* | |
* If error is populated, the promise was rejected or threw an exception. | |
* If data is populated, the promise resolved successfully. | |
*/ | |
export interface DoTryResult<T> { | |
error: any; | |
data: T; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment