
Security News
The Changelog Podcast: Practical Steps to Stay Safe on npm
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.
A small and magical composer for all JavaScript asynchronous.
ES5+, support node.js and browsers.
And a mountain of applications in server-side or client-side.
thunk is a function that encapsulates synchronous or asynchronous code inside.
thunk accepts only one callback function as an arguments, which is a CPS function.
thunk returns another thunk function after being called, for chaining operations.
thunk passes the results into a callback function after being excuted.
If the return value of callback is a thunk function, then it will be executed first and its result will be sent to another thunk for excution, or it will be sent to another new thunk function as the value of the computation.
const thunk = require('thunks')()
const fs = require('fs')
thunk(function (done) {
fs.stat('package.json', done)
})(function (error, res) {
console.log(error, res)
})
thunk(async function () {
console.log(await Promise.resolve('await promise in an async function'))
try {
await new Promise((resolve, reject) => {
setTimeout(() => reject('catch promise error in async function'), 1000)
})
} catch (err) {
console.log(err)
}
})()
const thunk = require('thunks')()
const fs = require('fs')
const size = thunk.thunkify(fs.stat)
// generator
thunk(function * () {
// yield thunk function
console.log(yield size('thunks.js'))
console.log(yield size('package.json'))
// yield async function
console.log(yield async () => 'yield an async function in generator function')
// yield generator function
console.log(yield function * () { return 'yield an async function in generator function' })
// parallel run
console.log(yield thunk.all([
size('thunks.js'),
size('package.json')
]))
})()
const thunk = require('thunks')()
const fs = require('fs')
const size = thunk.thunkify(fs.stat)
// sequential
size('.gitignore')(function (error, res) {
console.log(error, res)
return size('thunks.js')
})(function (error, res) {
console.log(error, res)
return size('package.json')
})(function (error, res) {
console.log(error, res)
})
// sequential
thunk.seq([
size('.gitignore'),
size('thunks.js'),
size('package.json')
])(function (error, res) {
console.log(error, res)
})
// parallel
thunk.all([
size('.gitignore'),
size('thunks.js'),
size('package.json')
])(function (error, res) {
console.log(error, res)
})
Node.js:
npm install thunks
Bower:
bower install thunks
browser:
<script src="/pathTo/thunks.js"></script>
const thunks = require('thunks')
const { thunks, thunk, slice, Scope, isAsyncFn, isGeneratorFn, isThunkableFn } = from 'thunks'
Matrix of thunk, it generates a thunkFunction factory (named thunk) with it's scope.
"scope" refers to the running evironments thunk generated(directly or indirectly) for all child thunk functions.
thunk, any exceptions would be passed the next child thunk function:const thunk = thunks()
thunk listening to all exceptions in current scope with onerror, and it will make sure the exceptions are not being passed to the followed child thunk function, unless onerror function returns true.const thunk = thunks(function (error) { console.error(error) })
Equals:
const scope = new thunks.Scope(function (error) { console.error(error) })
const thunk = thunks(scope)
thunk with onerror, onstop and debug listeners. Results of this thunk would be passed to debug function first before passing to the followed child thunk function.const thunk = thunks({
onstop: function (sig) { console.log(sig) },
onerror: function (error) { console.error(error) },
debug: function () { console.log.apply(console, arguments) }
})
Equals:
const scope = new thunks.Scope({
onstop: function (sig) { console.log(sig) },
onerror: function (error) { console.error(error) },
debug: function () { console.log.apply(console, arguments) }
})
const thunk = thunks(scope)
The context of onerror, onstop and debug is a scope.
Even multiple thunk main functions with different scopes are composed,
each scope would be separate from each other,
which means, onerror, onstop and debug would not run in other scopes.
Default to true, means it will prune error stack message.
Default to null, it is a global error handler.
const scope = new thunks.Scope({
onstop: function (sig) { assert.strictEqual(this, scope) },
onerror: function (error) { assert.strictEqual(this, scope) },
debug: function () { assert.strictEqual(this, scope) }
})
const thunk = thunks(scope)
This is the thunkFunction factory, to create new thunkFunction functions.
The parameter thunkable value could be:
thunkFunction function, by calling this function a new thunkFunction function will be returnedlet thunk1 = thunk(1)
thunk(thunk1)(function (error, value) {
console.log(error, value) // null 1
})
function (callback) {}, when called, passes its results to the next thunkFunction functionthunk(function (callback) {
callback(null, 1)
})(function (error, value) {
console.log(error, value) // null 1
})
thunkFunction functionlet promise = Promise.resolve(1)
thunk(promise)(function (error, value) {
console.log(error, value) // null 1
})
toThunklet obj = {
toThunk: function () {
return function (done) { done(null, 1) }
}
}
// `obj` has `toThunk` method that returns a thunk function
thunk(obj)(function (error, value) {
console.log(error, value) // null 1
})
toPromiseconst Rx = require('rxjs')
// Observable instance has `toPromise` method that returns a promise
thunk(Rx.Observable.fromPromise(Promise.resolve(123)))(function (error, value) {
console.log(error, value) // null 123
})
co, but yield anythingthunk(function * () {
var x = yield 10
return 2 * x
})(function * (error, res) {
console.log(error, res) // null, 20
return yield thunk.all([1, 2, thunk(3)])
})(function * (error, res) {
console.log(error, res) // null, [1, 2, 3]
return yield thunk.all({
name: 'test',
value: thunk(1)
})
})(function (error, res) {
console.log(error, res) // null, {name: 'test', value: 1}
})
thunk(async function () {
console.log(await Promise.resolve('await promise in an async function'))
try {
await new Promise((resolve, reject) => {
setTimeout(() => reject('catch promise error in async function'), 1000)
})
} catch (err) {
console.log(err)
}
})(function * () {
console.log(yield async () => 'yield an async function in generator function')
})()
thunk(1)(function (error, value) {
console.log(error, value) // null 1
})
thunk([1, 2, 3])(function (error, value) {
console.log(error, value) // null [1, 2, 3]
})
You can also run with this:
thunk.call({x: 123}, 456)(function (error, value) {
console.log(error, this.x, value) // null 123 456
return 'thunk!'
})(function (error, value) {
console.log(error, this.x, value) // null 123 'thunk!'
})
Returns a child thunk function.
obj can be an array or an object that contains any value. thunk.all will transform value to a child thunk function and excute it in parallel. After all of them are finished, an array containing results(in its original order) would be passed to the a new child thunk function.
thunk.all([
thunk(0),
function * () { return yield 1 },
2,
thunk(function (callback) { callback(null, [3]) })
])(function (error, value) {
console.log(error, value) // null [0, 1, 2, [3]]
})
thunk.all({
a: thunk(0),
b: thunk(1),
c: 2,
d: thunk(function (callback) { callback(null, [3]) })
})(function (error, value) {
console.log(error, value) // null {a: 0, b: 1, c: 2, d: [3]}
})
You may also write code like this:
thunk.all.call({x: [1, 2, 3]}, [4, 5, 6])(function (error, value) {
console.log(error, this.x, value) // null [1, 2, 3] [4, 5, 6]
return 'thunk!'
})(function (error, value) {
console.log(error, this.x, value) // null [1, 2, 3] 'thunk!'
})
Returns a child thunk function.
thunkX can be any value, thunk.seq will transform value to a child thunk function and excute it in order. After all of them are finished, an array containing results(in its original order) would be passed to the a new child thunk function.
thunk.seq([
function (callback) {
setTimeout(function () {
callback(null, 'a', 'b')
}, 100)
},
thunk(function (callback) {
callback(null, 'c')
}),
[thunk('d'), function * () { return yield 'e' }], // thunk in array will be excuted in parallel
function (callback) {
should(flag).be.eql([true, true])
flag[2] = true
callback(null, 'f')
}
])(function (error, value) {
console.log(error, value) // null [['a', 'b'], 'c', ['d', 'e'], 'f']
})
or
thunk.seq(
function (callback) {
setTimeout(function () {
callback(null, 'a', 'b')
}, 100)
},
thunk(function (callback) {
callback(null, 'c')
}),
[thunk('d'), thunk('e')], // thunk in array will be excuted in parallel
function (callback) {
should(flag).be.eql([true, true])
flag[2] = true
callback(null, 'f')
}
)(function (error, value) {
console.log(error, value) // null [['a', 'b'], 'c', ['d', 'e'], 'f']
})
You may also write code like this:
thunk.seq.call({x: [1, 2, 3]}, 4, 5, 6)(function (error, value) {
console.log(error, this.x, value) // null [1, 2, 3] [4, 5, 6]
return 'thunk!'
})(function (error, value) {
console.log(error, this.x, value) // null [1, 2, 3] 'thunk!'
})
Returns a child thunk function with the value or error from one first completed.
Returns a new function that would return a child thunk function
Transform a fn function which is in Node.js style into a new function.
This new function does not accept a callback as an argument, but accepts child thunk functions.
const thunk = require('thunks')()
const fs = require('fs')
const fsStat = thunk.thunkify(fs.stat)
fsStat('thunks.js')(function (error, result) {
console.log('thunks.js: ', result)
})
fsStat('.gitignore')(function (error, result) {
console.log('.gitignore: ', result)
})
You may also write code with this:
let obj = {a: 8}
function run (x, callback) {
//...
callback(null, this.a * x)
}
let run = thunk.thunkify.call(obj, run)
run(1)(function (error, result) {
console.log('run 1: ', result)
})
run(2)(function (error, result) {
console.log('run 2: ', result)
})
lift comes from Haskell, it transforms a synchronous function fn into a new async function.
This new function will accept thunkable arguments, evaluate them, then run as the original function fn. The new function returns a child thunk function.
const thunk = require('thunks')()
function calculator (a, b, c) {
return (a + b + c) * 10
}
const calculatorT = thunk.lift(calculator)
let value1 = thunk(2)
let value2 = Promise.resolve(3)
calculatorT(value1, value2, 5)(function (error, result) {
console.log(result) // 100
})
You may also write code with this:
const calculatorT = thunk.lift.call(context, calculator)
it transforms thunkable value to a promise.
const thunk = require('thunks').thunk
thunk.promise(function * () {
return yield Promise.resolve('Hello')
}).then(function (res) {
console.log(res)
})
it transforms thunkable value to a persist thunk function, which can be called more than once with the same result(like a promise). The new function returns a child thunk function.
const thunk = require('thunks')()
let persistThunk = thunk.persist(thunk(x))
persistThunk(function (error, result) {
console.log(1, result) // x
return persistThunk(function (error, result) {
console.log(2, result) // x
return persistThunk
})
})(function (error, result) {
console.log(3, result) // x
})
You may also write code with this:
const persistThunk = thunk.persist.call(context, thunkable)
Return a child thunk function, this child thunk function will be called after delay milliseconds.
console.log('thunk.delay 500: ', Date.now())
thunk.delay(500)(function () {
console.log('thunk.delay 1000: ', Date.now())
return thunk.delay(1000)
})(function () {
console.log('thunk.delay end: ', Date.now())
})
You may also write code with this:
console.log('thunk.delay start: ', Date.now())
thunk.delay.call(this, 1000)(function () {
console.log('thunk.delay end: ', Date.now())
})
This will stop control flow process with a message similar to Promise's cancelable(not implemented yet). It will throw a stop signal object.
Stop signal is an object with a message and status === 19(POSIX signal SIGSTOP) and a special code. Stop signal can be caught by onstop, and aslo can be caught by try catch, in this case it will not trigger onstop.
const thunk = require('thunks')({
onstop: function (res) {
if (res) console.log(res.code, res.status, res) // SIGSTOP 19 { message: 'Stop now!' }
}
})
thunk(function (callback) {
thunk.stop('Stop now!')
console.log('It will not run!')
})(function (error, value) {
console.log('It will not run!', error)
})
thunk.delay(100)(function () {
console.log('Hello')
return thunk.delay(100)(function () {
thunk.stop('Stop now!')
console.log('It will not run!')
})
})(function (error, value) {
console.log('It will not run!')
})
This will cancel all control flow process in the current thunk's scope.
import * as assert from 'assert'
import { thunk, thunks, isGeneratorFn } from 'thunks'
// or: import * as thunks from 'thunks'
thunk(function * () {
assert.strictEqual(yield thunks()(1), 1)
assert.ok(isGeneratorFn(function * () {}))
while (true) {
yield function (done) { setTimeout(done, 1000) }
console.log('Dang!')
}
})()
thunks supports so many thunkable objects. There are three kind of functions:
function (callback) { callback(err, someValue) }function * () { yield something }async function () { await somePromise }thunks can't support common functions (non-thunk-like functions). thunks uses fn.length === 1 to recognize thunk-like functions.
Using a common function in this way will throw an error:
thunk(function () {})(function (err) {
console.log(1, err) // 1 [Error: Not thunkable function: function () {}]
})
thunk(function (a, b) {})(function (err) {
console.log(2, err) // 2 [Error: Not thunkable function: function (a, b) {}]
})
thunk(function () { let callback = arguments[0]; callback() })(function (err) {
console.log(3, err) // 3 [Error: Not thunkable function: function () { let callback = arguments[0]; callback() }]
})
thunk()(function () {
return function () {} // can't return a non-thunkable function.
})(function (err) {
console.log(4, err) // 4 [Error: Not thunkable function: function () {}]
})
So pay attention to that. We can't return a non-thunkable function in thunk. If we return a thunkable function, thunk will evaluate it as an asynchronous task.
thunks is licensed under the MIT license. Copyright © 2014-2020 thunks.
FAQs
A small and magical composer for all JavaScript asynchronous.
The npm package thunks receives a total of 19,515 weekly downloads. As such, thunks popularity was classified as popular.
We found that thunks demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.

Security News
Experts push back on new claims about AI-driven ransomware, warning that hype and sponsored research are distorting how the threat is understood.

Security News
Ruby's creator Matz assumes control of RubyGems and Bundler repositories while former maintainers agree to step back and transfer all rights to end the dispute.