GithubHelp home page GithubHelp logo

weaklings's Introduction

weaklings

A library to make your code stronger by making it a little weaker.

Motivation

We recently discovered a memory leak due to the use of a of short-lived (per-HTTP request) cache that had periodic item expiry. The item expiry was controlled by a setTimeout. We forgot to close out the cache, assuming garbage collection would do all of the work we needed. Of course, it didn't, and setTimeout maintained a reference to the object so it was never GC'd.

This library is a small attempt to create some weak primitives that can be used to avoid this problem in the future.

Like all libraries around GC, it's probably better if you don't use these at all, but there could be use-cases where such tooling comes in handy.

API

Note that you although you can import straight from the weaklings package, the weaklings/timeout and weaklings/interval have some memory overhead in bookkeeping. You should import from those directly to only incur the costs that you need.

weakCallback

Behavior:

Accepting an object and a function (typically a callback) such that the function will only be called if the object still exists.

Import:

import { weakCallback } from 'weaklings/callback'

Declaration:

export function weakCallback<T, U, V>(obj: T, fn: (obj: T, ...args: U) => V): (...args: U) => V

Example, when run with node --expose-gc:

let someObj = { val: 0 }

const cb = weakCallback(someObj, (someObj, inc) => {
  someObj.val += inc
  console.log('val incremented', someObj.val)
})

// Note that the first argument to the wrapped function will always be the
// object, and the remaining arguments will be passed afterwards so that you
// don't hold a closure to it in your function.
cb(10)
// val incremented 10
cb(2)
// val incremented 12

someObj = null
gc() // force gc collection

cb(3)
// wrapped callback never called

Note: If you're using this from within a class, be sure that you do not form an arrow-closure over the class instance. For example:

class SomeTransientThing {
  doThing() { /* ... */ }

  makeTransientCallback() {
    // First parameter to the wrapped function cannot be called `this` -- it's
    // a syntax error. Thus `self`.
    return weakCallback(this, self => {
      self.doThing()
    })
  }
}

If the above used:

makeTransientCallback() {
  return weakCallback(this, () => {
    this.doThing()
  })
}

Then the lambda function itself would contain a closure over this, thus holding a reference to the object.

setWeakTimeout, clearWeakTimeout

Behavior:

Creates a timeout that will only trigger if the referenced object hasn't been GC'd.

Import:

import { setWeakTimeout, clearWeakTimeout } from 'weaklings/timeout'

Declaration:

export function setWeakTimeout<T extends object, U>(obj: T, fn: (obj: T, ...args: U) => unknown, timeout: number, ...args: U): WeakTimeout
export function clearWeakTimeout(timeout: WeakTimeout)

Use:

class EasilyGarbageCollectedCache {
  constructor(lifetime, fetch) {
    this.lifetime = lifetime
    this.fetch = fetch
    this.cache = new Map()
  }

  get(key) {
    let cached = this.cache.get(key)
    if (cached) {
      clearWeakTimeout(cached.timeout)
    }
    if (!cached) {
      cached = {
        value: this.fetch(key),
        timeout: undefined
      }
    }
    cached.timeout = setWeakTimeout(this, self => {
      self.clear(key)
    }, this.lifetime)
    return cached.value
  }

  clear(key) {
    const cached = this.cache.get(key)
    if (cached) {
      clearWeakTimeout(cached.timeout)
      this.cache.delete(key)
    }
  }
}

The above will create a cache that auto-purges entries at a given interval to reduce memory usage, and itself should be garbage-collectable without needing to remember to clear all of the associated timeouts.

setWeakInterval, clearWeakInterval

Behavior:

Similar to setWeakTimeout and clearWeakTimeout, but operates with intervals.

Import:

import { setWeakInterval, clearWeakInterval } from 'weaklings/interval'

Declaration:

export function setWeakInterval<T extends object, U>(obj: T, fn: (obj: T, ...args: U) => unknown, timeout: number, ...args: U): WeakTimeout
export function clearWeakInterval(timeout: WeakTimeout)

No example, use the the same as you would with setInterval.

weaklings's People

Contributors

twooster avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.