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

safe-recurse

v0.1.0

Published

Safe recursion in js

Downloads

2

Readme

safe-recurse

A hobby non-production ready project to deal with recursion call stack limits.

Why

The default call stack size in nodejs is around 10k calls.
(tested by running (function qwe(n=0) { console.log(n++); qwe(n) })() in node v13.14.0)

Here, I write a call stack error safe (or safer) alternative for your recursive functions.
Instead of getting a call stack size error, you should either get:

  • A nodejs native out of memory exception
  • The result of your function

For the moment, this library works only and only with single primitive argument functions, read the why single primitive argument only to understand why.

How to use it

npm/yarn/pnpm install safe-recurse package.

Here's a Fibonacci number example usage:

// fibonacci.js
const safeRecurse = require('safe-recurse')
const { run: fib, request, resolve } = safeRecurse(async (n) => {
  if (n === 0) {
    return resolve(n, 0)
  }
  if (n === 1) {
    return resolve(n, 1)
  }
  const [n1, n2] = await request([n - 1, n - 2])
  return resolve(n, n1 + n2)
})

await fib(6) // => 8

As shown in the code,

When we have the result for a call without recursion, we return resolve(argument, result).
For example: fib(1) = 1, then we do return resolve(1, 1)

When we need values we would otherwise get by recursing them, we await request(values) instead.
For example: fib(2) = fib(1) + fib(0), then we do await request([1, 0]) to obtain those values.

How does this work?

Fixing recursion

To fix the problem of stack size overflow we need to transform a programatically recursive function into a programatically iterated function. This iteration is done over a recursion loop.

While a recursive function calls itself and switches execution context before returning, an iterative alternative keep track of arguments passed in a list, and switches context after returning, when the next iteration starts and the function is called with the next argument.

This means instead of doing f(f(f())), we run them serially f()->f()->f().

In js, we make use of async/await to defer the results we haven't processed yet so that the end user code is relatively simple to write.

For the same reason, the code of this library was also relatively simple to write.

Results promises object

When it runs, safe-recurse keeps an object called results promises object.

This object has promises of results keyed by JSON.stringify(argument).

For example, for fibonacci the promise for the result of fib(1) is a promise with key '1' in the results promises object.

This effectively creates a dependency tree between results promises, in the same way fib(2) depends on fib(1) and fib(0).

Limitations

Performance

Performance is not the main interest of this project, at least initially.

No deadlocks checks

safe-recurse does not check for deadlocks in the recursion algorithm you pass to it, but an implementation for it could definitely be implemented.

No await before calling request in your recursive function

awaiting before request makes the request call to not get reached before the recursion loop iteration continues. When that happens, the iteration loop can run out of arguments to iterate next and the original promise never resolves.

It is possible for this to be implemented (note to self: by removing the async identifier and checking the type of Promise returned), but I don't think at this point that's really important.

Why single primitive argument only

(By primitive, we mean primitive values except undefined and Symbol)

I could add support for multiple argument recursion functions, but I wanted to focus first on single argument first.

Implementation of non-primitive arguments (objects for example) is pretty hard because it's not trivial to key objects.
For example, in js {a: 3} is not strictly equal to another object {a: 3}.
If we were to JSON.stringify them, order of properties can ruin strict equality, for example '{"a":3,"b":4}' is not equal to '{"b":4,"a":3}'.
So we would have ensure order of properties, and also deep check that every value is JSON.stringify safe.