A basic asynchronous utilily module beyond Promise magically.
Thinking and programming in thunks is similar to that in native Promise. But there are some different points:
-
Native Promise is a new feature in ES6, thunks is not using JavaScript special features to run flawlessly under ES3.
-
After wrapped by Promise we get objects with logics in Promise objects, methods of properties of these Promise objects could be modified(injuected); While thunks returns thunk functions, with logics inside function scopes, which means they would never be injected from outside.
-
Promise claims it's functional, while thunks is functional and it obeys the continuous-passing style.
-
Having the same power as Promise, thunks' API is more consice, and thunks' implementaton is simpler.
-
thunks brings a perfect
debugmode, which seems not appear in Promise? -
thunks is 6 times faster as native Promise.
Read in exmples/ diretory for more demos on thunks.
Build asynchronous program in an extraordinary simple way.
You don't have to wait till all bowsers have implemented Promise natively, but by just these 200sloc you will get more powerful tools for handling aynchronous code.
-
thunkis a function that encapsulates synchronous or asynchronous code inside. -
thunkaccepts only onecallbackfunction as an arguments, which is a CPS function; -
thunkreturns anotherthunkfunction after being called, for chaining operations; -
thunkwould passing the results into acallbackfunction after excuted. -
If
callbackreturns a newthunkfunction, then it would be send to anotherthunkto excute, or it would be send to another newthunkfunction as the value of the computation.
By running node benchmark/index.js in a CentOS virtual machine:
[root@centos thunk]# node benchmark/index
Sync Benchmark...
JSBench Start (100 cycles, async mode):
Test Promise...
Test thunk...
JSBench Results:
Promise: 100 cycles, 406.61 ms/cycle, 2.459 ops/sec
thunk: 100 cycles, 94.57 ms/cycle, 10.574 ops/sec
Promise: 100%; thunk: 429.96%;
JSBench Completed!
By testing with the same operations, Thunk performs 4 times faster comparing to native Promise.
var thunks = require('../thunks.js');
var fs = require('fs');
var Thunk = thunks(function (error) { console.error('Thunk error:', error); });
Thunk.
all(['examples/demo.js', 'thunks.js', '.gitignore'].map(function (path) {
return Thunk(function (callback) { fs.stat(path, callback); });
}))(function (error, result) {
console.log('Success: ', result);
return Thunk(function (callback) { fs.stat('none.js', callback); });
})(function (error, result) {
console.error('This should not run!', error);
});No Maximum call stack size exceeded error in 1000000 sync series
var Thunk = require('../thunks.js')();
var thunk = Thunk(0);
function callback(error, value) {
return ++value;
}
console.time('Thunk_series');
for (var i = 0; i < 1000000; i++) {
thunk = thunk(callback);
}
thunk(function (error, value) {
console.log(error, value); // null 1000000
console.timeEnd('Thunk_series'); // ~1468ms
});Node.js:
npm install thunks
Bower:
bower install thunks
browser:
<script src="/pathTo/thunks.js"></script>var thunks = require('thunks');Generator of thunks, it generates the main function of Thunk with its scope.
"scope" refers to the running evironments Thunk generated(directly or indirectly) for all thunk functions.
-
Here's how you create a basic
Thunk, any exceptions would be passed the nextthunkfunction:var Thunk = thunks();
-
Here's the way to create a
Thunklistening to all exceptions in current scope withonerror, and it will make sure the exeptions not being passed to the followedthunkfunction.var Thunk = thunks(function (error) { console.error(error); });
-
Create a
Thunkwithonerroranddebuglisteners. Results of thisThunkwould be passed todebugfunction first before passing to the followedthunkfunction.var Thunk = thunks({ 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 and debug would not run in other scopes.
This is the main function, to create new thunk functions.
The parameter start could be:
-
a
thunkfunction, by calling this function a newthunkfunction will be returnedvar 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 nextthunkfunctionThunk(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
thunkfunctionvar promise = Promise.resolve(1); Thunk(promise)(function (error, value) { console.log(error, value); // null 1 });
-
objects which implements methods of
toThunkvar then = Thenjs(1); // then.toThunk() return a thunk function Thunk(then)(function (error, value) { console.log(error, value); // null 1 });
-
values in other types would be valid results passing to a new
thunkfunctionThunk(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:
```js
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 thunk function.
obj can be an array or an object that contains several thunk functions or Promise objects.
They would be excuted at the same time. After all of them are finished,
an array containing results(in its original order) would be passed to the a new thunk function.
Thunk.all([
Thunk(0),
Thunk(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 thunk function.
Transform a Node.js callback function into a thunk function.
This thunk function retuslts in (error, val1, val2, ...), which is just being passed to a new 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 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 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);
});Return a thunk function, this 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`:
```js
console.log('Thunk.delay start: ', Date.now());
Thunk.delay.call(this, 1000)(function () {
console.log('Thunk.delay end: ', Date.now());
});