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 🙏

© 2025 – Pkg Stats / Ryan Hefner

ntil

v1.2.2

Published

create a handler to call a function until the result is good

Downloads

9

Readme

ntil

Create a handler to call a function 'ntil the result is good.

Use cases for "ntil"

If you've used Gmail, you'll have seen how it retries a network connection when it fails to retrieve your updated inbox. First it tries after a second, then a couple of seconds, then a while longer, etc. This is exactly what "ntil" will do for your code, whether on NodeJS or on the browser (it's designed for both).

How "ntil" works

The "ntil" function returns a wrapped version of your function to be retried after waiting 1 second, then 2, then 4, then 8, then 16 and finally after 32. (You can override the defaults of course). It works in an async manner, so that your function can do something useful over the network without blocking.

You provide a "check" function to return true when your function delivers results that pass your post-conditions. You may also optionally provide "done" and "fail" callback functions to handle the results of your function call. Please see the examples below to understand how it works.

Installing "ntil"

npm install --save ntil

Using "ntil"

This package provides a single method "ntil()" which is called thus:

ntil(checker, performer, success, failure, opts)

  • checker - a function to check a result and return true or false
  • performer - a function to call to perform a task which may succeed or fail
  • success - an optional function to process the result of a successful call
  • failure - an optional function to process the result of a failed call
  • opts - an optional hash of options (see below)

ntil() will return a handler that may be called with any number of arguments. The performer function will receive these arguments, with a final "next" arg appended to the argument list, such that it should be called on completion, passing the result (as a single argument or multiple arguments) thus:


var ntil = require('ntil');
var handler = ntil(
  function(result) { return result === 3 },               // the checker
  function myFunc(a, b, next) { next(a + b) },            // the performer
  function(result) { console.log('success! ' + result) }, // on success
  function(result) { console.log('failure! ' + result) }, // on failure
  {logger: console}                                       // options
);

handler(1, 1); // this will fail after 7 attempts (taking about a minute)
handler(1, 2); // this will succeed immediately

...and here's the equivalent code in a syntax more similar to jQuery:


var ntil = require('./ntil');
var handler = ntil(
  function(result) { return result === 3 }
).exec(
  function myFunc(a, b, next) { next(a + b) }
).done(
  function(result) { console.log('success! ' + result) }
).fail(
  function(result) { console.log('failure! ' + result) }
).opts(
  {logger: console}
).func();

handler(1, 1); // this will fail after 7 attempts (taking about a minute)
handler(1, 2); // this will succeed immediately

Note that the logger includes "myFunc" in log messages, because the function is named. An alternative is to use the "name" option (see below).

The "checker" function checks that the result is 3, causing the first handler to fail (it has a result of 2, not 3) and the second handler to succeed.

The output from both these examples is:


perform: 1 failure - trying again in 1 seconds
perform: success
success! 3
perform: 2 failures - trying again in 2 seconds
perform: 3 failures - trying again in 4 seconds
perform: 4 failures - trying again in 8 seconds
perform: 5 failures - trying again in 16 seconds
perform: 6 failures - trying again in 32 seconds
perform: too many failures (7)
failure! 2

The options may optionally include:

  • name: The name of the "performer" function we're calling, e.g. "getData"
  • logger: A logger object that responds to "info" and "warn" method calls
  • waitSecs: The initial duration in seconds to wait before retrying
  • waitMult: The factor by which to multiply the wait duration upon each retry
  • maxCalls: The maximum number of calls to make before failing

Note that "waitSecs" defaults to 1, "waitMult" defaults to 2, and "maxCalls" defaults to 7.

Here's an example that passes 2 result parameters:


var ntil = require('./ntil');
var handler = ntil(
  function(add, sub) { return add === 3 && sub === 1 }
).exec(
  function perform(a, b, next) { next(a + b, a - b) }
).done(
  function(add, sub) { console.log('success! %d %d', add, sub) }
).fail(
  function(add, sub) { console.log('failure! %d %d', add, sub) }
).opts(
  {logger: console, maxCalls: 3, waitSecs: 2, waitMult: 1}
).func();

handler(1, 1); // fails after 3 attempts taking 6 seconds
handler(1, 2); // fails after 3 attempts taking 6 seconds
handler(2, 1); // succeeds immediately

In this example, only the final call to handler(2, 1) will succeed.

Ideas for improvement? Email [email protected]