A small and magical async control flow tool, wrap promise, generator and anything to thunk.
ES3+, support node.js and all browsers.
- Toa: A powerful web framework rely on thunks.
- thunk-redis A thunk/promise-based redis client with pipelining and cluster.
- thunk-stream Wrap a readable/writable/duplex/transform stream to a thunk.
- thunk-queue A thunk queue for uncertainty tasks evaluation.
- file-cache Read file with caching, rely on thunks.
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 onecallback
function as an arguments, which is a CPS function. -
thunk
returns anotherthunk
function after being called, for chaining operations. -
thunk
would passing the results into acallback
function after excuted. -
If
callback
returns a newthunk
function, then it would be send to anotherthunk
to excute, or it would be send to another newthunk
function as the value of the computation.
var thunk = require('../thunks.js')()
var fs = require('fs')
var size = thunk.thunkify(fs.stat)
// generator
thunk(function *() {
// sequential
console.log(yield size('.gitignore'))
console.log(yield size('thunks.js'))
console.log(yield size('package.json'))
})(function *(error, res) {
//parallel
console.log(yield [
size('.gitignore'),
size('thunks.js'),
size('package.json')
])
})()
var thunk = require('../thunks.js')()
var fs = require('fs')
var 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)
})
// parallel
thunk.all([
size('.gitignore'),
size('thunks.js'),
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)
})
Node.js:
npm install thunks
Bower:
bower install thunks
browser:
<script src="/pathTo/thunks.js"></script>
var thunks = require('thunks')
Matrix of thunk
, it generates a thunk
generator function with it's scope.
"scope" refers to the running evironments thunk
generated(directly or indirectly) for all child thunk functions.
- Here's how you create a basic
thunk
, any exceptions would be passed the next child thunk function:
var thunk = thunks()
- Here's the way to create a
thunk
listening to all exceptions in current scope withonerror
, and it will make sure the exeptions not being passed to the followed child thunk function, unlessonerror
function returntrue
.
var thunk = thunks(function (error) { console.error(error) })
- Create a
thunk
withonerror
,onstop
anddebug
listeners. Results of thisthunk
would be passed todebug
function first before passing to the followed child thunk function.
var thunk = thunks({
onstop: function (sig) { console.log(sig) },
onerror: function (error) { console.error(error) },
debug: function () { console.log.apply(console, arguments) }
})
Even multiple thunk
main functions with diferent scope are composed,
each scope would be seperated from each other,
which means, onerror
, onstop
and debug
would not run in other scopes.
This is the main function, named thunk generator
, to create new child thunk functions.
The parameter thunkable
value could be:
- a child thunk function, by calling this function a new child thunk function will be returned
var thunk1 = thunk(1)
var thunk2 = thunk(thunk1) // thunk2 equals to thunk1
function (callback) {}
, by calling it, results woule be gathered and be passed to the next child thunk function
thunk(function (callback) {
callback(null, 1)
})(function (error, value) {
console.log(error, value) // null 1
})
- a Promise object, results of Promise would be passed to a new child thunk function
var promise = Promise.resolve(1)
thunk(promise)(function (error, value) {
console.log(error, value) // null 1
})
- objects which implements methods of
toThunk
var then = Thenjs(1) // then.toThunk() return a thunk function
thunk(then)(function (error, value) {
console.log(error, value) // null 1
})
- Generator and Generator Function, like
co
, andyield
anything
thunk(function *() {
var x = yield 10
return 2 * x
})(function *(error, res) {
console.log(error, res) // null, 20
return yield [1, 2, thunk(3)]
})(function *(error, res) {
console.log(error, res) // null, [1, 2, 3]
return yield {
name: 'test',
value: thunk(1)
}
})(function (error, res) {
console.log(error, res) // null, {name: 'test', value: 1}
})
- values in other types would be valid results passing to a new child thunk 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 excuted 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 excuted 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 child thunk function.
Transform a Node.js callback function into a child thunk function.
This child thunk function retuslts in (error, val1, val2, ...)
, which is just being passed to a new child thunk function,
like:
thunk(function (callback) {
callback(error, val1, val2, ...)
})
One use case:
thunk(function (callback) {
//...
callback(error, result)
})(function (error, value) {
//...
return thunk.digest(error, value)
})(function (error, value) {
//...
})
You may also write code with this
:
var a = {x: 1}
thunk.digest.call(a, null, 1, 2)(function (error, value1, value2) {
console.log(this, error, value1, value2) // { x: 1 } null 1 2
})
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 callback
as arguments, but accepts child thunk functions.
var thunk = require('../thunks.js')()
var fs = require('fs')
var 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
:
var obj = {a: 8}
function run (x, callback) {
//...
callback(null, this.a * x)
}
var 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 transform a sync 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 return a child thunk function.
var thunk = require('../thunks.js')()
function calculator (a, b, c) {
return (a + b + c) * 10
}
var calculatorT = thunk.lift(calculator)
var value1 = thunk(2)
var value2 = Promise.resolve(3)
calculatorT(value1, value2, 5)(function (error, result) {
console.log(result) // 100
})
You may also write code with this
:
var calculatorT = thunk.lift.call(context, calculator)
it transform thunkable
value to a persist thunk function, which can be called more than once with the same result(like as promise). The new function return a child thunk function.
var thunk = require('../thunks.js')()
var 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
:
var 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 implement yet). It will throw a stop signal object.
Stop signal is a 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
.
var thunk = require('thunks')({
debug: function (res) {
if (res) console.log(res) // { [Error: Stop now!] code: {}, status: 19 }
}
})
thunk(function (callback) {
thunk.stop('Stop now!')
console.log('It will not be run!')
})(function (error, value) {
console.log('It will not be run!')
})
thunk.delay(100)(function () {
console.log('Hello')
return thunk.delay(100)(function () {
thunk.stop('Stop now!')
console.log('It will not be run!')
})
})(function (error, value) {
console.log('It will not be run!')
})
- Teambition: https://www.teambition.com/ use in server-side and client-side