Created
May 19, 2022 18:00
-
-
Save peat-psuwit/d880af168215174a2ad92c1b4182f06c 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
function bindWeakImpl< | |
TFn extends (...args: any) => any, | |
TTarget extends object, | |
>(fn: TFn, weakTarget: WeakRef<TTarget>, defaultReturn: ReturnType<TFn>) | |
{ | |
return function(...args: Parameters<TFn>): ReturnType<TFn> { | |
const target = weakTarget.deref(); | |
if (target === undefined) { | |
console.debug(`bindWeak: target of ${fn} is now unreachable. Return ${defaultReturn}`); | |
return defaultReturn; | |
} | |
return fn.call(target, args); | |
} | |
} | |
function bindWeak< | |
TFn extends (this: TTarget, ...args: any) => any, | |
TTarget extends object, | |
>(fn: TFn, target: TTarget, defaultReturn: ReturnType<TFn>) { | |
const weakTarget = new WeakRef(target); | |
// Why need impl, you asked? Because if we return an anonymous function from right here, it | |
// _could_ hold a reference to the target despite not using it, defeating the use of WeakRef. | |
// Even though we don't actually refer to the target inside this function, better be safe than | |
// sorry. See https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156 | |
return bindWeakImpl(fn, weakTarget, defaultReturn); | |
} | |
// Demo time! | |
class Native { | |
// Imagine that this clas is actually e.g. a Gobject-introspection object. | |
// The cb field below would then be a GC root, preventing gabarge collection. | |
cb?: (x: number) => void; | |
setCb(cb: (x: number) => void) { | |
this.cb = cb; | |
} | |
} | |
class Wrapper { | |
native: Native; | |
constructor() { | |
this.native = new Native(); | |
// If nothing else refers to Wrapper, since the reference in this cb is weak, Wrapper | |
// can be collected, and so does the Native object and this cb. | |
this.native.setCb(bindWeak(this.cb, this, /* defaultReturn */ undefined)); | |
} | |
cb(x: number) { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment