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 🙏

© 2026 – Pkg Stats / Ryan Hefner

gocsp-thunk

v0.0.6

Published

Thunk as a compatible alternative to Promise

Readme

gocsp-thunk

Thunk as a compatible alternative to Promise

Install

npm install gocsp-thunk

Example

var thunk = require('gocsp-thunk')

// create a thunk function
var thunkFn = thunk(function (cb) {
    setTimeout(function () {
        cb(null, 'Hi')
    }, 100)
})

// get value from thunk function
thunkFn(function (err, val) {
    console.log(val) // => 'Hi'
})

// convert thunk function into native Promise
new Promise(thunkFn).then(console.log) // => 'Hi'

Thunk <=> Promise

It's super easy to convert one to the other.

  • do thunk.from(promise) to convert promise to thunk function
  • do new Promise(thunkFn) to convert thunk function to promise

Note: you should not use any falsy value (false, null, undefined, 0, etc) as exception.

Thunk vs Promise

Both thunk and promise are immutable and eager (execution). But thunk has following difference:

  • Thunk has no chaining. Unlike promise.then, thunkFn(cb) will NOT return another thunk. Use generator / asyncFn solution to resolve sequential thunks or promises.
  • Thunk is synchronously. It will callback synchronously whenever data is ready.
  • Thunk has no static methods like .all, .race. The equivalence are provided by different modules:
  • Thunk will not catch exception within execute function (from thunk( executeFn )).

Error Isolation

The error / exception within cb should not affect others.

The basic policy of error handling within callbacks is not allowing exception. If it does, the error will be caught and re-throw in next tick, which may crash program (add listener to prevent crash, e.g. process.on('uncaughtException', listener)).

Example:

var thunkFn = thunk(function (cb) {
    setTimeout(function () {
        cb(null, 123)
    }, 1000)
})
thunkFn(function cb() {
    // this exception will be caught
    // and re-throw in nextTick
    throw new Error()
})
// add listener to process to prevent potential crash
process.on('uncaughtException', listener)

It will be troublesome to have sync / async callbacks without error isolation.

Uncaught Exception

Thunk should not swallow exceptions.

If a thunkFn is rejected (e.g. cb(new Error)) and it has no listeners (callbacks), it will wait until next tick, if still no listeners, it will throw error globally, which probably will crash the program. To prevent crash, you can simply add a noop listener to thunk function or add a listener on process.on('uncaughtException', listener).

Example:

// thunk function has no listener, but it is rejected
// therefore, it will be re-throw in next tick if there
// is no listener at that time
var thunk = require('gocsp-thunk')
var thunkFn = thunk(function (cb) {
    cb(new Error)
})

Add noop listener to prevent crash:

thunkFn(function noop() {})

Add listener on process:

process.on('uncaughtException', function (err) {
    // ...
})

Sync or Async ?

thunkFunction(cb) will invoke cb as soon as data is ready, which means if data is already there, cb will be invoke immediately / synchronously.

You may notice that this will lead the execution of cb be indeterministic (aka. zalgo). Then following code will help you to check if it's sync or async.

var called = false
thunk(function () {
    called = true
    // exception is isolated, even you
    // throw exception here, it will not
    // affect outside of this callback function
})
if (called) {
    // it's sync
} else {
    // it's async
}

Also, the coroutine solution will help to determine the order of execution, as following.

var co = require('gocsp-co')
co(function* () {
    // do first
    yield thunkFunction // wait
    // do last
})()

Another problem with sync call is stack overflow when deep recursive sync call. Hope this could be solved by ES6 proper tail call ?

Anyway, you can always convert thunk function to promise if you want to ensure zalgo-free.

new Promise(thunkFunction).then(function () {
    // do it async
})

Cancellation

You could cancel the thunk operation

Example:

function timeout(time) {
    var ref
    return thunk(function init(done) {
        ref = setTimeout(done, time)
    }, onCancel() {
        clearTimeout(ref)
    })
}

var fn = timeout(1000)
fn(function () {
    doSomethingCool()
})

fn('cancel')

API Reference

thunk( executor )

Return a thunk function for deferred and asynchronous computations. Similar to new Promise( executor ), but executor only has one argument cb. Usually, use cb(error) for rejecting, and use cb(null, value) for fulfilling.

Example:

var thunk = require('gocsp-thunk')
var thunkFn = thunk(function (cb) {
    // do some work
    cb(null, 'I am done')
})
// get value from thunk function.
// call `thunkFn` multiple times will get the same result
thunkFn(function (err, val) {
    assert(val === 'I am done')
})
// wrap node style callbacks as thunk
thunk(cb => fs.readFile('path', 'utf8', cb))

new Promise( thunkFunction )

Convert a thunk function to a Promise instance

// convert to Native Promise
var thunk = require('gocsp-thunk')
new Promise(thunk(function (cb) {
    cb(null, 10)
}))
.then(function (val) {
    assert(val === 10)
})

thunk.from( promise )

Convert a Promise instance to a thunk function.

Example:

var thunk = require('gocsp-thunk')
var thunkFn = thunk.from(Promise.resolve(10))
thunkFn(function (err, val) {
    assert(val === 10)
})

thunk.isThunk( object )

Check if an object is thunk function.

Example:

var thunk = require('gocsp-thunk')
thunk.isThunk(123) // => false
thunk.isThunk(function () {}) // => false
thunk.isThunk(thunk(cb => cb())) // => true

thunk.ify( fn ) or thunk.thunkify( fn )

Wrap node style function (callback as last argument) to one which returns a thunk

Example:

var thunk = require('gocsp-thunk')
var readFile = thunk.ify(require('fs').readFile)
readFile(__filename, 'utf8')(function (err, val) {
    if (err) { throw err }
    console.log(val)
})

thunk.ifyAll( object ) or thunk.thunkifyAll( object )

Wrap object with node style function as property or in prototype chain into new object with all thunkifed methods.

Example:

var redis = require('redis')
var co = require('gocsp-co')
var thunk = require('gocsp-thunk')

var client = thunk.ifyAll(redis.createClient())

co(function *(){
    yield client.set('foo', '123')
    yield client.set('bar', '456')

    console.log('get foo:', yield client.get('foo')) // => 123
    console.log('get bar:', yield client.get('bar')) // => 456

    console.log(yield client.quit())
})()

Inspiration

License

MIT