A basic asynchronous utilily module beyond Promise magically, support generator.
Repo was moved to https://github.com/thunks/thunks
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 5 times faster as native Promise.
-
Full implementation with Generator.
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 300sloc 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.
➜ thunks git:(master) ✗ node --harmony benchmark/index
Sync Benchmark...
JSBench Start (1000 cycles, async mode):
Test Promise...
Test co...
Test thunks-generator...
Test bluebird...
Test when...
Test RSVP...
Test async...
Test thenjs...
Test thunks...
JSBench Results:
co: 1000 cycles, 29.334 ms/cycle, 34.090 ops/sec
Promise: 1000 cycles, 27.786 ms/cycle, 35.989 ops/sec
async: 1000 cycles, 2.631 ms/cycle, 380.084 ops/sec
RSVP: 1000 cycles, 1.621 ms/cycle, 616.903 ops/sec
when: 1000 cycles, 1.423 ms/cycle, 702.741 ops/sec
bluebird: 1000 cycles, 1.036 ms/cycle, 965.251 ops/sec
thenjs: 1000 cycles, 0.767 ms/cycle, 1303.781 ops/sec
thunks: 1000 cycles, 0.698 ms/cycle, 1432.665 ops/sec
thunks-generator: 1000 cycles, 0.514 ms/cycle, 1945.525 ops/sec
co: 100%; Promise: 105.57%; async: 1114.94%; RSVP: 1809.62%; when: 2061.42%; bluebird: 2831.47%; thenjs: 3824.51%; thunks: 4202.58%; thunks-generator: 5707.00%;
JSBench Completed!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);
})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')]);
})();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, unlessonerrorfunction returntrue.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 });
-
Generator and Generator Function, like
co, andyieldanythingThunk(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
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 any value. Thunk.all will transform value to a 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 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 thunk function.
thunkX can be any value, Thunk.seq will transform value to a 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 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 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:
console.log('Thunk.delay start: ', Date.now());
Thunk.delay.call(this, 1000)(function () {
console.log('Thunk.delay end: ', Date.now());
});