npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

weaklings

v0.0.1

Published

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

Downloads

3

Readme

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

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.