- Notifications
You must be signed in to change notification settings - Fork40
A small and magical composer for all JavaScript asynchronous.
License
thunks/thunks
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
A small and magical composer for all JavaScript asynchronous.
ES5+, support node.js and browsers.
- thunks
- Compatibility
- Summary
- Implementations
- What is a thunk
- Demo
- Installation
- API
- thunks([scope])
- thunks.pruneErrorStack
- thunks.onerror(error)
- Class thunks.Scope
- thunk(thunkable)
- thunk.all(obj)
- thunk.all(thunkable1, ..., thunkableN)
- thunk.seq([thunkable1, ..., thunkableN])
- thunk.seq(thunkable1, ..., thunkableN)
- thunk.race([thunkable1, ..., thunkableN])
- thunk.race(thunkable1, ..., thunkableN)
- thunk.thunkify(fn)
- thunk.lift(fn)
- thunk.promise(thunkable)
- thunk.persist(thunkable)
- thunk.delay(delay)
- thunk.stop([message])
- thunk.cancel()
- TypeScript Typings
- What functions are thunkable
- License
- Toa A powerful web framework rely on thunks.
- T-man Super test manager for JavaScript.
- thunk-redis The fastest thunk/promise-based redis client, support all redis features.
- thunk-disque A thunk/promise-based disque client.
- thunk-stream Wrap a readable/writable/duplex/transform stream to a thunk.
- thunk-queue A thunk queue for uncertainty tasks evaluation.
- thunk-loop Asynchronous tasks loop (while (true) { ... }).
- thunk-mocha Enable support for generators in Mocha with backward compatibility.
- thunk-ratelimiter The fastest abstract rate limiter.
- thunk-workers Thunk-based task scheduler that executes synchrounous and/or asynchronous tasks under concurrency control.
- file-cache Read file with caching, rely on thunks.
And a mountain of applications in server-side or client-side.
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.thunkpasses the results into acallbackfunction after being excuted.If the return value of
callbackis athunkfunction, then it will be executed first and its result will be sent to anotherthunkfor excution, or it will be sent to another newthunkfunction as the value of the computation.
constthunk=require('thunks')()constfs=require('fs')thunk(function(done){fs.stat('package.json',done)})(function(error,res){console.log(error,res)})
thunk(asyncfunction(){console.log(awaitPromise.resolve('await promise in an async function'))try{awaitnewPromise((resolve,reject)=>{setTimeout(()=>reject('catch promise error in async function'),1000)})}catch(err){console.log(err)}})()
constthunk=require('thunks')()constfs=require('fs')constsize=thunk.thunkify(fs.stat)// generatorthunk(function*(){// yield thunk functionconsole.log(yieldsize('thunks.js'))console.log(yieldsize('package.json'))// yield async functionconsole.log(yieldasync()=>'yield an async function in generator function')// yield generator functionconsole.log(yieldfunction*(){return'yield an async function in generator function'})// parallel runconsole.log(yieldthunk.all([size('thunks.js'),size('package.json')]))})()
constthunk=require('thunks')()constfs=require('fs')constsize=thunk.thunkify(fs.stat)// sequentialsize('.gitignore')(function(error,res){console.log(error,res)returnsize('thunks.js')})(function(error,res){console.log(error,res)returnsize('package.json')})(function(error,res){console.log(error,res)})// sequentialthunk.seq([size('.gitignore'),size('thunks.js'),size('package.json')])(function(error,res){console.log(error,res)})// parallelthunk.all([size('.gitignore'),size('thunks.js'),size('package.json')])(function(error,res){console.log(error,res)})
Node.js:
npm install thunksBower:
bower install thunksbrowser:
<scriptsrc="/pathTo/thunks.js"></script>
constthunks=require('thunks')
const{ thunks, thunk, slice, Scope, isAsyncFn, isGeneratorFn, isThunkableFn}=from'thunks'
Matrix ofthunk, it generates athunkFunction factory (namedthunk) with it's scope."scope" refers to the running evironmentsthunk 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:
constthunk=thunks()
- Here's the way to create a
thunklistening to all exceptions in current scope withonerror, and it will make sure the exceptions are not being passed to the followed child thunk function, unlessonerrorfunction returnstrue.
constthunk=thunks(function(error){console.error(error)})
Equals:
constscope=newthunks.Scope(function(error){console.error(error)})constthunk=thunks(scope)
- Create a
thunkwithonerror,onstopanddebuglisteners. Results of thisthunkwould be passed todebugfunction first before passing to the followed child thunk function.
constthunk=thunks({onstop:function(sig){console.log(sig)},onerror:function(error){console.error(error)},debug:function(){console.log.apply(console,arguments)}})
Equals:
constscope=newthunks.Scope({onstop:function(sig){console.log(sig)},onerror:function(error){console.error(error)},debug:function(){console.log.apply(console,arguments)}})constthunk=thunks(scope)
The context ofonerror,onstop anddebug is ascope.Even multiplethunk main functions with different scopes are composed,each scope would be separate from each other,which means,onerror,onstop anddebug would not run in other scopes.
Default totrue, means it will prune error stack message.
Default tonull, it is a global error handler.
constscope=newthunks.Scope({onstop:function(sig){assert.strictEqual(this,scope)},onerror:function(error){assert.strictEqual(this,scope)},debug:function(){assert.strictEqual(this,scope)}})constthunk=thunks(scope)
This is thethunkFunction factory, to create newthunkFunction functions.
The parameterthunkable value could be:
- a
thunkFunctionfunction, by calling this function a newthunkFunctionfunction will be returned
letthunk1=thunk(1)thunk(thunk1)(function(error,value){console.log(error,value)// null 1})
- a thunkLike function
function (callback) {}, when called, passes its results to the nextthunkFunctionfunction
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
thunkFunctionfunction
letpromise=Promise.resolve(1)thunk(promise)(function(error,value){console.log(error,value)// null 1})
- objects which implements the method
toThunk
letobj={toThunk:function(){returnfunction(done){done(null,1)}}}// `obj` has `toThunk` method that returns a thunk functionthunk(obj)(function(error,value){console.log(error,value)// null 1})
- objects which implement the method
toPromise
constRx=require('rxjs')// Observable instance has `toPromise` method that returns a promisethunk(Rx.Observable.fromPromise(Promise.resolve(123)))(function(error,value){console.log(error,value)// null 123})
- Generator and Generator Function, like
co, butyieldanything
thunk(function*(){varx=yield10return2*x})(function*(error,res){console.log(error,res)// null, 20returnyieldthunk.all([1,2,thunk(3)])})(function*(error,res){console.log(error,res)// null, [1, 2, 3]returnyieldthunk.all({name:'test',value:thunk(1)})})(function(error,res){console.log(error,res)// null, {name: 'test', value: 1}})
- async/await function
thunk(asyncfunction(){console.log(awaitPromise.resolve('await promise in an async function'))try{awaitnewPromise((resolve,reject)=>{setTimeout(()=>reject('catch promise error in async function'),1000)})}catch(err){console.log(err)}})(function*(){console.log(yieldasync()=>'yield an async function in generator function')})()
- values in other types that would be valid results to pass 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 withthis:
thunk.call({x:123},456)(function(error,value){console.log(error,this.x,value)// null 123 456return'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*(){returnyield1},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*(){returnyield'e'}],// thunk in array will be excuted in parallelfunction(callback){should(flag).be.eql([true,true])flag[2]=truecallback(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 parallelfunction(callback){should(flag).be.eql([true,true])flag[2]=truecallback(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 afn function which is in Node.js style into a new function.This new function does not accept acallback as an argument, but accepts child thunk functions.
constthunk=require('thunks')()constfs=require('fs')constfsStat=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 withthis:
letobj={a:8}functionrun(x,callback){//...callback(null,this.a*x)}letrun=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 functionfn into a new async function.This new function will acceptthunkable arguments, evaluate them, then run as the original functionfn. The new function returns a child thunk function.
constthunk=require('thunks')()functioncalculator(a,b,c){return(a+b+c)*10}constcalculatorT=thunk.lift(calculator)letvalue1=thunk(2)letvalue2=Promise.resolve(3)calculatorT(value1,value2,5)(function(error,result){console.log(result)// 100})
You may also write code withthis:
constcalculatorT=thunk.lift.call(context,calculator)
it transformsthunkable value to a promise.
constthunk=require('thunks').thunkthunk.promise(function*(){returnyieldPromise.resolve('Hello')}).then(function(res){console.log(res)})
it transformsthunkable 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.
constthunk=require('thunks')()letpersistThunk=thunk.persist(thunk(x))persistThunk(function(error,result){console.log(1,result)// xreturnpersistThunk(function(error,result){console.log(2,result)// xreturnpersistThunk})})(function(error,result){console.log(3,result)// x})
You may also write code withthis:
constpersistThunk=thunk.persist.call(context,thunkable)
Return a child thunk function, this child thunk function will be called afterdelay milliseconds.
console.log('thunk.delay 500: ',Date.now())thunk.delay(500)(function(){console.log('thunk.delay 1000: ',Date.now())returnthunk.delay(1000)})(function(){console.log('thunk.delay end: ',Date.now())})
You may also write code withthis:
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 andstatus === 19(POSIX signal SIGSTOP) and a special code. Stop signal can be caught byonstop, and aslo can be caught bytry catch, in this case it will not triggeronstop.
constthunk=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')returnthunk.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*asassertfrom'assert'import{thunk,thunks,isGeneratorFn}from'thunks'// or: import * as thunks from 'thunks'thunk(function*(){assert.strictEqual(yieldthunks()(1),1)assert.ok(isGeneratorFn(function*(){}))while(true){yieldfunction(done){setTimeout(done,1000)}console.log('Dang!')}})()
thunks supports so manythunkable objects. There are three kind of functions:
- thunk-like function
function (callback) { callback(err, someValue) } - generator function
function * () { yield something } - async/await function
async function () { await somePromise }
thunks can't support common functions (non-thunk-like functions). thunks usesfn.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(){letcallback=arguments[0];callback()})(function(err){console.log(3,err)// 3 [Error: Not thunkable function: function () { let callback = arguments[0]; callback() }]})thunk()(function(){returnfunction(){}// 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 theMIT license.Copyright © 2014-2020 thunks.
About
A small and magical composer for all JavaScript asynchronous.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Contributors9
Uh oh!
There was an error while loading.Please reload this page.