{"id":13472918,"url":"https://github.com/thunks/thunks","last_synced_at":"2025-12-12T03:03:46.053Z","repository":{"id":17881012,"uuid":"20823709","full_name":"thunks/thunks","owner":"thunks","description":"A small and magical composer for all JavaScript asynchronous.","archived":true,"fork":false,"pushed_at":"2023-04-15T20:24:27.000Z","size":672,"stargazers_count":528,"open_issues_count":2,"forks_count":47,"subscribers_count":24,"default_branch":"master","last_synced_at":"2024-10-21T06:13:35.104Z","etag":null,"topics":["async-await","asynchronous","callback","generator","promise","thunk"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thunks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2014-06-14T02:55:53.000Z","updated_at":"2024-08-20T06:02:41.000Z","dependencies_parsed_at":"2023-07-16T04:17:04.637Z","dependency_job_id":null,"html_url":"https://github.com/thunks/thunks","commit_stats":{"total_commits":166,"total_committers":9,"mean_commits":"18.444444444444443","dds":0.06024096385542166,"last_synced_commit":"eb1a42cddc2a2720780106915882463aa6d5659a"},"previous_names":["teambition/thunk"],"tags_count":97,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunks%2Fthunks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunks%2Fthunks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunks%2Fthunks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunks%2Fthunks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thunks","download_url":"https://codeload.github.com/thunks/thunks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222156534,"owners_count":16940436,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async-await","asynchronous","callback","generator","promise","thunk"],"created_at":"2024-07-31T16:00:59.141Z","updated_at":"2025-10-22T18:34:16.699Z","avatar_url":"https://github.com/thunks.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","others"],"sub_categories":[],"readme":"# thunks\n\nA small and magical composer for all JavaScript asynchronous.\n\n[![NPM version][npm-image]][npm-url]\n[![Build Status][travis-image]][travis-url]\n[![js-standard-style][js-standard-image]][js-standard-url]\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[中文说明](https://github.com/thunks/thunks/blob/master/docs/api-zh.md)\n\n[thunks 的作用域和异常处理设计](https://github.com/thunks/thunks/blob/master/docs/scope-and-error-catch.md)\n\n## Compatibility\n\nES5+, support node.js and browsers.\n\n## Summary\n\n- [thunks](#thunks)\n  - [Compatibility](#compatibility)\n  - [Summary](#summary)\n  - [Implementations](#implementations)\n  - [What is a thunk](#what-is-a-thunk)\n  - [Demo](#demo)\n    - [with thunk function](#with-thunk-function)\n    - [with async function](#with-async-function)\n    - [with generator function](#with-generator-function)\n    - [chain, sequential, parallel](#chain-sequential-parallel)\n  - [Installation](#installation)\n  - [API](#api)\n    - [thunks([scope])](#thunksscope)\n    - [thunks.pruneErrorStack](#thunkspruneerrorstack)\n    - [thunks.onerror(error)](#thunksonerrorerror)\n    - [Class thunks.Scope](#class-thunksscope)\n    - [thunk(thunkable)](#thunkthunkable)\n    - [thunk.all(obj)](#thunkallobj)\n    - [thunk.all(thunkable1, ..., thunkableN)](#thunkallthunkable1--thunkablen)\n    - [thunk.seq([thunkable1, ..., thunkableN])](#thunkseqthunkable1--thunkablen)\n    - [thunk.seq(thunkable1, ..., thunkableN)](#thunkseqthunkable1--thunkablen)\n    - [thunk.race([thunkable1, ..., thunkableN])](#thunkracethunkable1--thunkablen)\n    - [thunk.race(thunkable1, ..., thunkableN)](#thunkracethunkable1--thunkablen)\n    - [thunk.thunkify(fn)](#thunkthunkifyfn)\n    - [thunk.lift(fn)](#thunkliftfn)\n    - [thunk.promise(thunkable)](#thunkpromisethunkable)\n    - [thunk.persist(thunkable)](#thunkpersistthunkable)\n    - [thunk.delay(delay)](#thunkdelaydelay)\n    - [thunk.stop([message])](#thunkstopmessage)\n    - [thunk.cancel()](#thunkcancel)\n  - [TypeScript Typings](#typescript-typings)\n  - [What functions are thunkable](#what-functions-are-thunkable)\n  - [License](#license)\n\n## Implementations\n\n- [Toa](https://github.com/toajs/toa) A powerful web framework rely on thunks.\n- [T-man](https://github.com/thunks/tman) Super test manager for JavaScript.\n- [thunk-redis](https://github.com/thunks/thunk-redis) The fastest thunk/promise-based redis client, support all redis features.\n- [thunk-disque](https://github.com/thunks/thunk-disque) A thunk/promise-based disque client.\n- [thunk-stream](https://github.com/thunks/thunk-stream) Wrap a readable/writable/duplex/transform stream to a thunk.\n- [thunk-queue](https://github.com/thunks/thunk-queue) A thunk queue for uncertainty tasks evaluation.\n- [thunk-loop](https://github.com/thunks/thunk-loop) Asynchronous tasks loop (while (true) { ... }).\n- [thunk-mocha](https://github.com/thunks/thunk-mocha) Enable support for generators in Mocha with backward compatibility.\n- [thunk-ratelimiter](https://github.com/thunks/thunk-ratelimiter) The fastest abstract rate limiter.\n- [thunk-workers](https://github.com/thunks/thunk-workers) Thunk-based task scheduler that executes synchrounous and/or asynchronous tasks under concurrency control.\n- [file-cache](https://github.com/thunks/file-cache) Read file with caching, rely on thunks.\n\nAnd a mountain of applications in server-side or client-side.\n\n## What is a thunk\n\n1. [ALGOL thunks in 1961](https://web.archive.org/web/20190415165418/https://portalparts.acm.org/1070000/1064045/fm/frontmatter.pdf?ip=98.14.66.142)\n\n1. **`thunk`** is a function that encapsulates synchronous or asynchronous code inside.\n\n1. **`thunk`** accepts only one `callback` function as an arguments, which is a CPS function.\n\n1. **`thunk`** returns another **`thunk`** function after being called, for chaining operations.\n\n1. **`thunk`** passes the results into a `callback` function after being excuted.\n\n1. If the return value of `callback` is a **`thunk`** function, then it will be executed first and its result will be sent to another **`thunk`** for excution, or it will be sent to another new **`thunk`** function as the value of the computation.\n\n## Demo\n\n### with thunk function\n\n```js\nconst thunk = require('thunks')()\nconst fs = require('fs')\n\nthunk(function (done) {\n  fs.stat('package.json', done)\n})(function (error, res) {\n  console.log(error, res)\n})\n```\n\n### with async function\n\n```js\nthunk(async function () {\n  console.log(await Promise.resolve('await promise in an async function'))\n\n  try {\n    await new Promise((resolve, reject) =\u003e {\n      setTimeout(() =\u003e reject('catch promise error in async function'), 1000)\n    })\n  } catch (err) {\n    console.log(err)\n  }\n})()\n```\n\n### with generator function\n\n```js\nconst thunk = require('thunks')()\nconst fs = require('fs')\nconst size = thunk.thunkify(fs.stat)\n\n// generator\nthunk(function * () {\n  // yield thunk function\n  console.log(yield size('thunks.js'))\n  console.log(yield size('package.json'))\n\n  // yield async function\n  console.log(yield async () =\u003e 'yield an async function in generator function')\n\n  // yield generator function\n  console.log(yield function * () { return 'yield an async function in generator function' })\n\n    // parallel run\n  console.log(yield thunk.all([\n    size('thunks.js'),\n    size('package.json')\n  ]))\n})()\n```\n\n### chain, sequential, parallel\n\n```js\nconst thunk = require('thunks')()\nconst fs = require('fs')\nconst size = thunk.thunkify(fs.stat)\n\n// sequential\nsize('.gitignore')(function (error, res) {\n  console.log(error, res)\n  return size('thunks.js')\n\n})(function (error, res) {\n  console.log(error, res)\n  return size('package.json')\n\n})(function (error, res) {\n  console.log(error, res)\n})\n\n// sequential\nthunk.seq([\n  size('.gitignore'),\n  size('thunks.js'),\n  size('package.json')\n])(function (error, res) {\n  console.log(error, res)\n})\n\n// parallel\nthunk.all([\n  size('.gitignore'),\n  size('thunks.js'),\n  size('package.json')\n])(function (error, res) {\n  console.log(error, res)\n})\n```\n\n## Installation\n\n**Node.js:**\n\n    npm install thunks\n\n**Bower:**\n\n    bower install thunks\n\n**browser:**\n\n```html\n\u003cscript src=\"/pathTo/thunks.js\"\u003e\u003c/script\u003e\n```\n\n## API\n\n```js\nconst thunks = require('thunks')\n```\n\n```js\nconst { thunks, thunk, slice, Scope, isAsyncFn, isGeneratorFn, isThunkableFn } = from 'thunks'\n```\n\n### thunks([scope])\n\nMatrix of `thunk`, it generates a `thunkFunction` factory (named `thunk`) with it's scope.\n\"scope\" refers to the running evironments `thunk` generated(directly or indirectly) for all child thunk functions.\n\n1. Here's how you create a basic `thunk`, any exceptions would be passed the next child thunk function:\n\n  ```js\n  const thunk = thunks()\n  ```\n\n1. Here's the way to create a `thunk` listening to all exceptions in current scope with `onerror`, and it will make sure the exceptions are not being passed to the followed child thunk function, unless `onerror` function returns `true`.\n\n  ```js\n  const thunk = thunks(function (error) { console.error(error) })\n  ```\n\n  **Equals:**\n  ```js\n  const scope = new thunks.Scope(function (error) { console.error(error) })\n  const thunk = thunks(scope)\n  ```\n\n1. 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.\n\n  ```js\n  const thunk = thunks({\n    onstop: function (sig) { console.log(sig) },\n    onerror: function (error) { console.error(error) },\n    debug: function () { console.log.apply(console, arguments) }\n  })\n  ```\n\n  **Equals:**\n  ```js\n  const scope = new thunks.Scope({\n    onstop: function (sig) { console.log(sig) },\n    onerror: function (error) { console.error(error) },\n    debug: function () { console.log.apply(console, arguments) }\n  })\n  const thunk = thunks(scope)\n  ```\nThe context of `onerror`, `onstop` and `debug` is a `scope`.\nEven multiple `thunk` main functions with different scopes are composed,\neach scope would be separate from each other,\nwhich means, `onerror`, `onstop` and `debug` would not run in other scopes.\n\n### thunks.pruneErrorStack\n\nDefault to `true`, means it will prune error stack message.\n\n### thunks.onerror(error)\n\nDefault to `null`, it is a global error handler.\n\n### Class thunks.Scope\n\n```js\nconst scope = new thunks.Scope({\n  onstop: function (sig) { assert.strictEqual(this, scope) },\n  onerror: function (error) { assert.strictEqual(this, scope) },\n  debug: function () { assert.strictEqual(this, scope) }\n})\nconst thunk = thunks(scope)\n```\n\n### thunk(thunkable)\n\nThis is the `thunkFunction` factory, to create new `thunkFunction` functions.\n\nThe parameter `thunkable` value could be:\n\n1. a `thunkFunction` function, by calling this function a new `thunkFunction` function will be returned\n\n  ```js\n  let thunk1 = thunk(1)\n  thunk(thunk1)(function (error, value) {\n    console.log(error, value) // null 1\n  })\n  ```\n\n1. a thunkLike function `function (callback) {}`, when called, passes its results to the next `thunkFunction` function\n\n  ```js\n  thunk(function (callback) {\n    callback(null, 1)\n  })(function (error, value) {\n    console.log(error, value) // null 1\n  })\n  ```\n\n1. a Promise object, results of Promise would be passed to a new `thunkFunction` function\n\n  ```js\n  let promise = Promise.resolve(1)\n\n  thunk(promise)(function (error, value) {\n    console.log(error, value) // null 1\n  })\n  ```\n\n1. objects which implements the method `toThunk`\n\n  ```js\n  let obj = {\n    toThunk: function () {\n      return function (done) { done(null, 1) }\n    }\n  }\n  // `obj` has `toThunk` method that returns a thunk function\n  thunk(obj)(function (error, value) {\n    console.log(error, value) // null 1\n  })\n  ```\n\n1. objects which implement the method `toPromise`\n\n  ```js\n  const Rx = require('rxjs')\n  // Observable instance has `toPromise` method that returns a promise\n  thunk(Rx.Observable.fromPromise(Promise.resolve(123)))(function (error, value) {\n    console.log(error, value) // null 123\n  })\n  ```\n\n1. Generator and Generator Function, like `co`, but `yield` anything\n\n  ```js\n  thunk(function * () {\n    var x = yield 10\n    return 2 * x\n  })(function * (error, res) {\n    console.log(error, res) // null, 20\n\n    return yield thunk.all([1, 2, thunk(3)])\n  })(function * (error, res) {\n    console.log(error, res) // null, [1, 2, 3]\n    return yield thunk.all({\n      name: 'test',\n      value: thunk(1)\n    })\n  })(function (error, res) {\n    console.log(error, res) // null, {name: 'test', value: 1}\n  })\n  ```\n\n1. async/await function\n\n  ```js\n  thunk(async function () {\n    console.log(await Promise.resolve('await promise in an async function'))\n\n    try {\n      await new Promise((resolve, reject) =\u003e {\n        setTimeout(() =\u003e reject('catch promise error in async function'), 1000)\n      })\n    } catch (err) {\n      console.log(err)\n    }\n  })(function * () {\n    console.log(yield async () =\u003e 'yield an async function in generator function')\n  })()\n  ```\n\n1. values in other types that would be valid results to pass to a new child thunk function\n\n  ```js\n  thunk(1)(function (error, value) {\n    console.log(error, value) // null 1\n  })\n\n  thunk([1, 2, 3])(function (error, value) {\n    console.log(error, value) // null [1, 2, 3]\n  })\n  ```\n\nYou can also run with `this`:\n\n  ```js\n  thunk.call({x: 123}, 456)(function (error, value) {\n    console.log(error, this.x, value) // null 123 456\n    return 'thunk!'\n  })(function (error, value) {\n    console.log(error, this.x, value) // null 123 'thunk!'\n  })\n  ```\n\n### thunk.all(obj)\n\n### thunk.all(thunkable1, ..., thunkableN)\n\nReturns a child thunk function.\n\n`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.\n\n```js\nthunk.all([\n  thunk(0),\n  function * () { return yield 1 },\n  2,\n  thunk(function (callback) { callback(null, [3]) })\n])(function (error, value) {\n  console.log(error, value) // null [0, 1, 2, [3]]\n})\n\nthunk.all({\n  a: thunk(0),\n  b: thunk(1),\n  c: 2,\n  d: thunk(function (callback) { callback(null, [3]) })\n})(function (error, value) {\n  console.log(error, value) // null {a: 0, b: 1, c: 2, d: [3]}\n})\n```\n\nYou may also write code like this:\n\n```js\nthunk.all.call({x: [1, 2, 3]}, [4, 5, 6])(function (error, value) {\n  console.log(error, this.x, value) // null [1, 2, 3] [4, 5, 6]\n  return 'thunk!'\n})(function (error, value) {\n  console.log(error, this.x, value) // null [1, 2, 3] 'thunk!'\n})\n```\n\n### thunk.seq([thunkable1, ..., thunkableN])\n\n### thunk.seq(thunkable1, ..., thunkableN)\n\nReturns a child thunk function.\n\n`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.\n\n```js\nthunk.seq([\n  function (callback) {\n    setTimeout(function () {\n      callback(null, 'a', 'b')\n    }, 100)\n  },\n  thunk(function (callback) {\n    callback(null, 'c')\n  }),\n  [thunk('d'), function * () { return yield 'e' }], // thunk in array will be excuted in parallel\n  function (callback) {\n    should(flag).be.eql([true, true])\n    flag[2] = true\n    callback(null, 'f')\n  }\n])(function (error, value) {\n  console.log(error, value) // null [['a', 'b'], 'c', ['d', 'e'], 'f']\n})\n```\n\nor\n\n```js\nthunk.seq(\n  function (callback) {\n    setTimeout(function () {\n      callback(null, 'a', 'b')\n    }, 100)\n  },\n  thunk(function (callback) {\n    callback(null, 'c')\n  }),\n  [thunk('d'), thunk('e')], // thunk in array will be excuted in parallel\n  function (callback) {\n    should(flag).be.eql([true, true])\n    flag[2] = true\n    callback(null, 'f')\n  }\n)(function (error, value) {\n  console.log(error, value) // null [['a', 'b'], 'c', ['d', 'e'], 'f']\n})\n```\n\nYou may also write code like this:\n\n```js\nthunk.seq.call({x: [1, 2, 3]}, 4, 5, 6)(function (error, value) {\n  console.log(error, this.x, value) // null [1, 2, 3] [4, 5, 6]\n  return 'thunk!'\n})(function (error, value) {\n  console.log(error, this.x, value) // null [1, 2, 3] 'thunk!'\n})\n```\n\n### thunk.race([thunkable1, ..., thunkableN])\n\n### thunk.race(thunkable1, ..., thunkableN)\n\nReturns a child thunk function with the value or error from one first completed.\n\n### thunk.thunkify(fn)\n\nReturns a new function that would return a child thunk function\n\nTransform a `fn` function which is in Node.js style into a new function.\nThis new function does not accept a `callback` as an argument, but accepts child thunk functions.\n\n```js\nconst thunk = require('thunks')()\nconst fs = require('fs')\nconst fsStat = thunk.thunkify(fs.stat)\n\nfsStat('thunks.js')(function (error, result) {\n  console.log('thunks.js: ', result)\n})\nfsStat('.gitignore')(function (error, result) {\n  console.log('.gitignore: ', result)\n})\n```\n\nYou may also write code with `this`:\n\n```js\nlet obj = {a: 8}\nfunction run (x, callback) {\n  //...\n  callback(null, this.a * x)\n}\n\nlet run = thunk.thunkify.call(obj, run)\n\nrun(1)(function (error, result) {\n  console.log('run 1: ', result)\n})\nrun(2)(function (error, result) {\n  console.log('run 2: ', result)\n})\n```\n\n### thunk.lift(fn)\n\n`lift` comes from Haskell, it transforms a synchronous function `fn` into a new async function.\nThis new function will accept `thunkable` arguments, evaluate them, then run as the original function `fn`. The new function returns a child thunk function.\n\n```js\nconst thunk = require('thunks')()\n\nfunction calculator (a, b, c) {\n  return (a + b + c) * 10\n}\n\nconst calculatorT = thunk.lift(calculator)\n\nlet value1 = thunk(2)\nlet value2 = Promise.resolve(3)\n\ncalculatorT(value1, value2, 5)(function (error, result) {\n  console.log(result) // 100\n})\n```\n\nYou may also write code with `this`:\n\n```js\nconst calculatorT = thunk.lift.call(context, calculator)\n```\n\n### thunk.promise(thunkable)\n\nit transforms `thunkable` value to a promise.\n\n```js\nconst thunk = require('thunks').thunk\n\nthunk.promise(function * () {\n  return yield Promise.resolve('Hello')\n}).then(function (res) {\n  console.log(res)\n})\n```\n\n### thunk.persist(thunkable)\n\nit transforms `thunkable` 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.\n\n```js\nconst thunk = require('thunks')()\n\nlet persistThunk = thunk.persist(thunk(x))\n\npersistThunk(function (error, result) {\n  console.log(1, result) // x\n  return persistThunk(function (error, result) {\n    console.log(2, result) // x\n    return persistThunk\n  })\n})(function (error, result) {\n  console.log(3, result) // x\n})\n```\n\nYou may also write code with `this`:\n\n```js\nconst persistThunk = thunk.persist.call(context, thunkable)\n```\n\n### thunk.delay(delay)\n\nReturn a child thunk function, this child thunk function will be called after `delay` milliseconds.\n\n```js\nconsole.log('thunk.delay 500: ', Date.now())\nthunk.delay(500)(function () {\n  console.log('thunk.delay 1000: ', Date.now())\n  return thunk.delay(1000)\n})(function () {\n  console.log('thunk.delay end: ', Date.now())\n})\n```\n\nYou may also write code with `this`:\n\n```js\nconsole.log('thunk.delay start: ', Date.now())\nthunk.delay.call(this, 1000)(function () {\n  console.log('thunk.delay end: ', Date.now())\n})\n```\n\n### thunk.stop([message])\n\nThis will stop control flow process with a message similar to Promise's cancelable(not implemented yet). It will throw a stop signal object.\nStop signal is an 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`.\n\n```js\nconst thunk = require('thunks')({\n  onstop: function (res) {\n    if (res) console.log(res.code, res.status, res) // SIGSTOP 19 { message: 'Stop now!' }\n  }\n})\n\nthunk(function (callback) {\n  thunk.stop('Stop now!')\n  console.log('It will not run!')\n})(function (error, value) {\n  console.log('It will not run!', error)\n})\n```\n\n```js\nthunk.delay(100)(function () {\n  console.log('Hello')\n  return thunk.delay(100)(function () {\n    thunk.stop('Stop now!')\n    console.log('It will not run!')\n  })\n})(function (error, value) {\n  console.log('It will not run!')\n})\n```\n\n### thunk.cancel()\n\nThis will cancel all control flow process in the current thunk's scope.\n\n## TypeScript Typings\n\n```typescript\nimport * as assert from 'assert'\nimport { thunk, thunks, isGeneratorFn } from 'thunks'\n// or: import * as thunks from 'thunks'\n\nthunk(function * () {\n  assert.strictEqual(yield thunks()(1), 1)\n  assert.ok(isGeneratorFn(function * () {}))\n\n  while (true) {\n    yield function (done) { setTimeout(done, 1000) }\n    console.log('Dang!')\n  }\n})()\n```\n\n## What functions are thunkable\n\nthunks supports so many [thunkable](#thunkthunkable) objects. There are three kind of functions:\n\n- thunk-like function `function (callback) { callback(err, someValue) }`\n- generator function `function * () { yield something }`\n- async/await function `async function () { await somePromise }`\n\nthunks can't support common functions (non-thunk-like functions). thunks uses `fn.length === 1` to recognize thunk-like functions.\n\nUsing a common function in this way will throw an error:\n\n```js\nthunk(function () {})(function (err) {\n  console.log(1, err) // 1 [Error: Not thunkable function: function () {}]\n})\n\nthunk(function (a, b) {})(function (err) {\n  console.log(2, err) // 2 [Error: Not thunkable function: function (a, b) {}]\n})\n\nthunk(function () { let callback = arguments[0]; callback() })(function (err) {\n  console.log(3, err) // 3 [Error: Not thunkable function: function () { let callback = arguments[0]; callback() }]\n})\n\nthunk()(function () {\n  return function () {} // can't return a non-thunkable function.\n})(function (err) {\n  console.log(4, err) // 4 [Error: Not thunkable function: function () {}]\n})\n```\n\nSo 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.\n\n## License\n\nthunks is licensed under the [MIT](https://github.com/thunks/thunks/blob/master/LICENSE) license.\nCopyright \u0026copy; 2014-2020 thunks.\n\n[npm-url]: https://npmjs.org/package/thunks\n[npm-image]: http://img.shields.io/npm/v/thunks.svg\n\n[travis-url]: https://travis-ci.org/thunks/thunks\n[travis-image]: http://img.shields.io/travis/thunks/thunks.svg\n\n[coveralls-url]: https://coveralls.io/r/thunks/thunks\n[coveralls-image]: https://coveralls.io/repos/thunks/thunks/badge.svg\n\n[downloads-url]: https://npmjs.org/package/thunks\n[downloads-image]: http://img.shields.io/npm/dm/thunks.svg?style=flat-square\n\n[js-standard-url]: https://github.com/feross/standard\n[js-standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthunks%2Fthunks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthunks%2Fthunks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthunks%2Fthunks/lists"}