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
debug
mode, 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.
-
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.
➜ 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 nextthunk
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 followedthunk
function, unlessonerror
function returntrue
.var Thunk = thunks(function (error) { console.error(error); });
-
Create a
Thunk
withonerror
anddebug
listeners. Results of thisThunk
would be passed todebug
function first before passing to the followedthunk
function.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
thunk
function, by calling this function a newthunk
function 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 nextthunk
functionThunk(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
thunk
functionvar 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
anythingThunk(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
thunk
functionThunk(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());
});