Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 17, 2023. It is now read-only.
/thunksPublic archive

A small and magical composer for all JavaScript asynchronous.

License

NotificationsYou must be signed in to change notification settings

thunks/thunks

Repository files navigation

A small and magical composer for all JavaScript asynchronous.

NPM versionBuild Statusjs-standard-styleCoverage StatusDownloads

中文说明

thunks 的作用域和异常处理设计

Compatibility

ES5+, support node.js and browsers.

Summary

Implementations

  • 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.

What is a thunk

  1. ALGOL thunks in 1961

  2. thunk is a function that encapsulates synchronous or asynchronous code inside.

  3. thunk accepts only onecallback function as an arguments, which is a CPS function.

  4. thunk returns anotherthunk function after being called, for chaining operations.

  5. thunk passes the results into acallback function after being excuted.

  6. If the return value ofcallback is athunk function, then it will be executed first and its result will be sent to anotherthunk for excution, or it will be sent to another newthunk function as the value of the computation.

Demo

with thunk function

constthunk=require('thunks')()constfs=require('fs')thunk(function(done){fs.stat('package.json',done)})(function(error,res){console.log(error,res)})

with async 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)}})()

with generator function

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')]))})()

chain, sequential, parallel

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)})

Installation

Node.js:

npm install thunks

Bower:

bower install thunks

browser:

<scriptsrc="/pathTo/thunks.js"></script>

API

constthunks=require('thunks')
const{ thunks, thunk, slice, Scope, isAsyncFn, isGeneratorFn, isThunkableFn}=from'thunks'

thunks([scope])

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.

  1. Here's how you create a basicthunk, any exceptions would be passed the next child thunk function:
constthunk=thunks()
  1. Here's the way to create athunk listening to all exceptions in current scope withonerror, and it will make sure the exceptions are not being passed to the followed child thunk function, unlessonerror function returnstrue.
constthunk=thunks(function(error){console.error(error)})

Equals:

constscope=newthunks.Scope(function(error){console.error(error)})constthunk=thunks(scope)
  1. Create athunk withonerror,onstop anddebug listeners. Results of thisthunk would be passed todebug function 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.

thunks.pruneErrorStack

Default totrue, means it will prune error stack message.

thunks.onerror(error)

Default tonull, it is a global error handler.

Class thunks.Scope

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)

thunk(thunkable)

This is thethunkFunction factory, to create newthunkFunction functions.

The parameterthunkable value could be:

  1. athunkFunction function, by calling this function a newthunkFunction function will be returned
letthunk1=thunk(1)thunk(thunk1)(function(error,value){console.log(error,value)// null 1})
  1. a thunkLike functionfunction (callback) {}, when called, passes its results to the nextthunkFunction function
thunk(function(callback){callback(null,1)})(function(error,value){console.log(error,value)// null 1})
  1. a Promise object, results of Promise would be passed to a newthunkFunction function
letpromise=Promise.resolve(1)thunk(promise)(function(error,value){console.log(error,value)// null 1})
  1. objects which implements the methodtoThunk
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})
  1. objects which implement the methodtoPromise
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})
  1. Generator and Generator Function, likeco, butyield anything
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}})
  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')})()
  1. 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!'})

thunk.all(obj)

thunk.all(thunkable1, ..., thunkableN)

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!'})

thunk.seq([thunkable1, ..., thunkableN])

thunk.seq(thunkable1, ..., thunkableN)

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!'})

thunk.race([thunkable1, ..., thunkableN])

thunk.race(thunkable1, ..., thunkableN)

Returns a child thunk function with the value or error from one first completed.

thunk.thunkify(fn)

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)})

thunk.lift(fn)

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)

thunk.promise(thunkable)

it transformsthunkable value to a promise.

constthunk=require('thunks').thunkthunk.promise(function*(){returnyieldPromise.resolve('Hello')}).then(function(res){console.log(res)})

thunk.persist(thunkable)

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)

thunk.delay(delay)

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())})

thunk.stop([message])

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!')})

thunk.cancel()

This will cancel all control flow process in the current thunk's scope.

TypeScript Typings

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!')}})()

What functions are thunkable

thunks supports so manythunkable objects. There are three kind of functions:

  • thunk-like functionfunction (callback) { callback(err, someValue) }
  • generator functionfunction * () { yield something }
  • async/await functionasync 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.

License

thunks is licensed under theMIT license.Copyright © 2014-2020 thunks.

About

A small and magical composer for all JavaScript asynchronous.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors9


[8]ページ先頭

©2009-2025 Movatter.jp