Is there an equivalent to Go's Defer statement in TypeScript?
I find it tedious to write cleanup code in various parts of a function. Searching for a simpler alternative.
I tried searching on Google, but couldn't locate any relevant information.
Is there an equivalent to Go's Defer statement in TypeScript?
I find it tedious to write cleanup code in various parts of a function. Searching for a simpler alternative.
I tried searching on Google, but couldn't locate any relevant information.
One could argue that the answer is no, however, there are a few alternatives available:
As mentioned by @bereal, you can utilize a try
/finally
for this purpose. In regards to your comment about avoiding the use of try-catch blocks due to them being expensive:
Yes, throwing an
Error
can be costly (especially in terms of stack info creation), but entering atry
block itself does not incur much overhead. Additionally, JavaScript allows for throwing items other than Error instances, although it's generally recommended to stick with Error. Thefinally
blocks do have minimal overhead, though recent tests in modern engines showed this to be negligible.
An alternative approach would be to assign a function to a variable and execute it at the conclusion of the function execution. While this method may be more costly than using try
/finally
for a single cleanup task, it could prove advantageous when handling multiple cleanups that might require nested try
/finally
blocks.
For illustration purposes, here are some sample scenarios:
Single cleanup using try
/finally
:
function example() {
try {
console.log("hello");
} finally {
console.log("world");
}
}
example();
Multiple cleanups using try
/finally
:
function example() {
try {
console.log("my");
try {
console.log("dog");
} finally {
console.log("has");
}
} finally {
console.log("fleas");
}
}
example();
Single cleanup via assigned function:
function example() {
let fn = null;
fn = () => console.log("world");
console.log("hello");
if (fn) { // Conditional check for the above function assignment
fn();
}
}
example();
Multiple cleanups using try
/finally
:
function example() {
const cleanup = [];
cleanup.push(() => console.log("has"));
console.log("my");
cleanup.push(() => console.log("fleas"));
console.log("dog");
cleanup.forEach(fn => fn());
}
example();
Alternatively:
function example() {
const cleanup = [];
cleanup.push(() => console.log("fleas"));
console.log("my");
cleanup.push(() => console.log("has"));
console.log("dog");
while (cleanup.length) {
const fn = cleanup.pop();
fn();
}
}
example();
If you are looking for a more concise method to implement a defer
functionality in TypeScript similar to Go's behavior, consider the following approach:
class Test {
@Defer()
async test () {
const timer = setInterval(() => console.log('interval'), 1000)
defer.call(this, () => clearInterval(timer))
await new Promise(resolve => setTimeout(resolve, 1500))
}
}
const t = new Test()
t.test()
.catch(console.error)
In the provided code snippet, a timer
is set to output 'interval' every second, and a defer
function is defined to clear the interval when exiting the function scope (similar to Go programming language).
Upon execution, the
await new Promise(resolve => setTimeout(resolve, 1500)
will introduce a delay of 1.5 seconds, resulting in one 'interval' output before program termination.
$ ts-node src/defer.ts
interval
$
The comprehensive code example showcased below demonstrates the implementation of this concept in TypeScript version 4.4:
const DEFER_SYMBOL = Symbol('defer')
type Callback = (err?: Error) => void
interface DeferizedThis {
[DEFER_SYMBOL]?: Callback[],
}
function Defer () {
return function callMethodDecorator (
_target : any,
_propertyKey : string,
descriptor : PropertyDescriptor,
): PropertyDescriptor {
const oldValue = descriptor.value
async function deferizedMethod (
this: DeferizedThis,
...args: any[]
) {
try {
const ret = await oldValue.apply(this, args)
return ret
} finally {
if (this[DEFER_SYMBOL]) {
const deferCallbacks = this[DEFER_SYMBOL]
while (true) {
const fn = deferCallbacks?.pop()
if (!fn) { break }
try { fn() } catch (e) { console.error(e) }
}
}
}
}
descriptor.value = deferizedMethod
return descriptor
}
}
function defer (
this: any,
cb: Callback,
): void {
if (this[DEFER_SYMBOL]) {
this[DEFER_SYMBOL]!.push(cb)
} else {
this[DEFER_SYMBOL] = [cb]
}
}
class Test {
@Defer()
async test () {
const timer = setInterval (()=> console.log ('interval'), 1000)
defer.call (this, () => clearInterval (timer ))
await new Promise (resolve => setTimeout (resolve, 1500))
}
}
const t = new Test ()
t.test ()
.catch (console.error )
This proof-of-concept code serves as an illustrative example and is not suitable for production use.
If you have suggestions on improving this approach or alternative methods in TypeScript, please feel free to share your insights for discussion.
Perhaps this insight will be of assistance, courtesy of the original response from T.J. Crowder (@t-j-crowder).
Implement deferral by utilizing variables in the finally block of a try/catch statement
function example() {
const defers = [];
try {
var xx = "First";
defers.push(((text) => () => console.log(text))(xx));
xx = "Not First";
var yy = "Last";
defers.push(((text) => () => console.log(text))(yy));
yy = "Not Last"
const timer = setInterval(() => console.log('live'), 1000);
defers.push(((t) => () => clearInterval(t))(timer));
} finally {
while (defers.length) {
const fn = defers.pop();
fn();
}
}
}
example();
This is the most suitable approach that we can take. Insert a call back in the middle of the function and then invoke that function at the end.
async function example() {
let completeFunction = () => {
console.log("The process has finished")
}
console.log("Starting the process");
await delay(2000);
console.log("Process completed");
completeFunction();
}
example()
function delay(ms) {
return new Promise(resolve=> setTimeout(resolve, ms))
}
class Alpha { static construct<T extends typeof Alpha>(this: T): InstanceType<T> { const v = new Alpha(); return v as InstanceType<T>; } } class Beta extends Alpha {} const x = Alpha.construct(); // generates Alpha const y = ...
How can I write different handlers in TypeScript for ThrottlingException and ExecutionLimitExceeded when starting a StepFunction execution? new StepFunction.startExecution({}, (err, data) => { if (err) { // Need to identify ThrottlingExcepti ...
Can you explain the variation between using the : syntax for declaring type let serverMessage: UServerMessage = message; and the as syntax? let serverMessage = message as UServerMessage; It appears that they yield identical outcomes in this particular ...
Click here for a hands-on example. Take a look at the code snippet below: export type BigType = { foo: number; bar?: number; baz: number; qux?: string[]; }; function BuildBigType(params: string[]) { // Here's what I'd like to do: ...
I'm attempting to obtain a computed value using the composition API in vue, and here is the code I am working with: setup() { const store = useStore(); const spaUrls = inject<SpaUrls>('spaUrls'); const azureAd = inject<AzureAd ...
Having trouble using guards for an unlogged user and constantly facing errors. Error: TS7030 - Not all code paths return a value. Below is my auth.guard.ts file: import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from &a ...
Here is the initial array object that I have: const data = [ { "order_id":"ORDCUTHIUJ", "branch_code":"MVPA", "total_amt":199500, "product_details":[ { ...
While I have some familiarity with TypeScript, there is one thing that continues to intrigue me. I understand the distinction between Array<string> and string[]. I am aware that these declarations can be used interchangeably, such as: export class S ...
When working in languages such as C#, managing memory is not a problem. However, this can lead to difficult-to-find memory bugs in other languages. Is it safe to use the following code snippet in Typescript or Javascript without encountering any issues? c ...
Is there a way in Typescript to reference a function type with generics without instantiating them, but rather passing them to be instantiated when the function is called? For instance, consider the following type: type FetchPageData<T> = (client : ...
My Node.js post API currently returns a token, but I want it to include the user's email, id, etc: app.post('/auth', function (req, response) { const body = req.body; console.log(req.body); let query = `select * from users wher ...
Currently overseeing the staging environment of a substantial project comprising over 50 dynamic pages. These pages undergo time-based revalidation every 5 minutes on Vercel's complimentary tier. In addition, I am tasked with importing data for numer ...
Behold this interesting item: const [object, setObject] = useState ({ item1: "Greetings, World!", item2: "Salutations!", }); I aim to retrieve all the children from it. I have a snippet of code here, but for some reason, i ...
My goal is to create a functionality where clicking on a button within a specific row opens up a matDialog box displaying all the contents of that row. Below is the HTML snippet: <tr *ngFor="let u of users"> <td data-label="ID& ...
My desired outcome (This does not conform to TS rules) : const type1 = "number"; let myVariable1 : typeof<type1> = 12; let type2 = "string" as const; let myVariable2 : typeof<type2> = "foo"; Is it possible to impl ...
For a while now, I've been quite at ease creating and utilizing NPM packages with Typescript. However, these packages have typically been provided and consumed as a single module. Now, I'm interested in publishing packages that contain more than ...
I am currently working with an arrayList that contains employee information such as employeename, grade, and designation. In my view, I have a multiselect component that returns an array of grades like [1,2,3] once we select grade1, grade2, grade3 from the ...
I am currently working with a function called search, which at the moment is set up to return a type of Promise<MyObject[]>: export function search(args: SearchInput) { return SomeInterface.performSearch(args) .then(xmlRequest =&g ...
How can I dynamically return an object key in array.map()? Currently, I am retrieving the maximum value from an array using a specific object key with the following code: Math.max.apply(Math, class.map(function (o) { return o.Students; })); In this code ...
Consider this TypeScript code snippet: module animals { export class Animal { } } module display.animals { export class DisplayAnimal extends animals.Animal { } } The objective here is to create a subclass called DisplayAnimal in the dis ...