Giter Club home page Giter Club logo

thunks's Introduction

thunks

A small and magical async control flow tool, wrap promise, generator and anything to thunk.

NPM version Build Status js-standard-style Coverage Status Downloads Talk topic

中文说明

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

Compatibility

ES3+, support node.js and all browsers.

Implementations:

  • Toa: A powerful web framework rely on thunks.
  • thunk-redis A thunk/promise-based redis client with pipelining and cluster.
  • thunk-stream Wrap a readable/writable/duplex/transform stream to a thunk.
  • thunk-queue A thunk queue for uncertainty tasks evaluation.
  • 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. thunk is a function that encapsulates synchronous or asynchronous code inside.

  2. thunk accepts only one callback function as an arguments, which is a CPS function.

  3. thunk returns another thunk function after being called, for chaining operations.

  4. thunk would passing the results into a callback function after excuted.

  5. If callback returns a new thunk function, then it would be send to another thunk to excute, or it would be send to another new thunk function as the value of the computation.

Demo

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

Installation

Node.js:

npm install thunks

Bower:

bower install thunks

browser:

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

API

var thunks = require('thunks')

thunks([options])

Matrix of thunk, it generates a thunk generator function with it's scope. "scope" refers to the running evironments thunk generated(directly or indirectly) for all child thunk functions.

  1. Here's how you create a basic thunk, any exceptions would be passed the next child thunk function:
var thunk = thunks()
  1. Here's the way to create a thunk listening to all exceptions in current scope with onerror, and it will make sure the exeptions not being passed to the followed child thunk function, unless onerror function return true.
var thunk = thunks(function (error) { console.error(error) })
  1. Create a thunk with onerror, onstop and debug listeners. Results of this thunk would be passed to debug function first before passing to the followed child thunk function.
var thunk = thunks({
  onstop: function (sig) { console.log(sig) },
  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, onstop and debug would not run in other scopes.

thunk(thunkable)

This is the main function, named thunk generator, to create new child thunk functions.

The parameter thunkable value could be:

  1. a child thunk function, by calling this function a new child thunk function will be returned
var thunk1 = thunk(1)
var thunk2 = thunk(thunk1) // thunk2 equals to thunk1
  1. function (callback) {}, by calling it, results woule be gathered and be passed to the next child thunk 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 new child thunk function
var promise = Promise.resolve(1)

thunk(promise)(function (error, value) {
  console.log(error, value) // null 1
})
  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
})
  1. Generator and Generator Function, like co, and yield anything
thunk(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}
})
  1. values in other types would be valid results passing 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 with this:

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

thunk.all(obj)

thunk.all(thunk1, ..., thunkX)

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

thunk.seq([thunk1, ..., thunkX])

thunk.seq(thunk1, ..., thunkX)

Returns a child thunk function.

thunkX can be any value, thunk.seq will transform value to a child 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 child 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!'
})

thunk.race([thunk1, ..., thunkX])

thunk.race(thunk1, ..., thunkX)

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

thunk.digest(error, val1, val2, ...)

Returns a child thunk function.

Transform a Node.js callback function into a child thunk function. This child thunk function retuslts in (error, val1, val2, ...), which is just being passed to a new child 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
})

thunk.thunkify(fn)

Returns a new function that would return a child 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 child 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)
})

thunk.lift(fn)

lift comes from Haskell, it transform a sync function fn into a new async function. This new function will accept thunkable arguments, evaluate them, then run as the original function fn. The new function return a child thunk function.

var thunk = require('../thunks.js')()

function calculator (a, b, c) {
  return (a + b + c) * 10
}

var calculatorT = thunk.lift(calculator)

var value1 = thunk(2)
var value2 = Promise.resolve(3)

calculatorT(value1, value2, 5)(function (error, result) {
  console.log(result) // 100
})

You may also write code with this:

var calculatorT = thunk.lift.call(context, calculator)

thunk.persist(thunkable)

it transform thunkable value to a persist thunk function, which can be called more than once with the same result(like as promise). The new function return a child thunk function.

var thunk = require('../thunks.js')()

var persistThunk = thunk.persist(thunk(x))

persistThunk(function (error, result) {
  console.log(1, result) // x
  return persistThunk(function (error, result) {
    console.log(2, result) // x
    return persistThunk
  })
})(function (error, result) {
  console.log(3, result) // x
})

You may also write code with this:

var persistThunk = thunk.persist.call(context, thunkable)

thunk.delay(delay)

Return a child thunk function, this child 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())
})

thunk.stop([messagge])

This will stop control flow process with a message similar to Promise's cancelable(not implement yet). It will throw a stop signal object. Stop signal is a object with a message and status === 19(POSIX signal SIGSTOP) and a special code. Stop signal can be caught by onstop, and aslo can be caught by try catch, in this case it will not trigger onstop.

var thunk = require('thunks')({
  debug: function (res) {
    if (res) console.log(res) // { [Error: Stop now!] code: {}, status: 19 }
  }
})

thunk(function (callback) {
  thunk.stop('Stop now!')
  console.log('It will not be run!')
})(function (error, value) {
  console.log('It will not be run!')
})
thunk.delay(100)(function () {
  console.log('Hello')
  return thunk.delay(100)(function () {
    thunk.stop('Stop now!')
    console.log('It will not be run!')
  })
})(function (error, value) {
  console.log('It will not be run!')
})

Who's using

thunks's People

Contributors

zensh avatar tiye avatar gyson avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.