├── .gitignore ├── History.md ├── LICENSE ├── Makefile ├── Readme.md ├── component.json ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 2.1.2 / 2014-06-02 3 | ================== 4 | 5 | * fix: make this an optimizable function with crankshaft 6 | 7 | 2.1.1 / 2014-04-17 8 | ================== 9 | 10 | * remove memoization 11 | 12 | 2.1.0 / 2014-04-17 13 | ================== 14 | 15 | * add catching of thunk errors 16 | 17 | 2.0.0 / 2014-04-17 18 | ================== 19 | 20 | * change to behave like a future (subsequent calls yield the same value) 21 | * remove support for eager execution (breaking change) 22 | 23 | 1.0.0 / 2014-04-11 24 | ================== 25 | 26 | * add assert(fn) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 TJ Holowaychuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @./node_modules/.bin/mocha \ 4 | --require should \ 5 | --reporter dot \ 6 | --bail 7 | 8 | .PHONY: test -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # thunkify 2 | 3 | Turn a regular node function into one which returns a thunk, 4 | useful for generator-based flow control such as [co](https://github.com/visionmedia/co). 5 | 6 | ## Installation 7 | 8 | ``` 9 | $ npm install thunkify 10 | ``` 11 | 12 | ## Example 13 | 14 | ```js 15 | var thunkify = require('thunkify'); 16 | var fs = require('fs'); 17 | 18 | var read = thunkify(fs.readFile); 19 | 20 | read('package.json', 'utf8')(function(err, str){ 21 | 22 | }); 23 | ``` 24 | 25 | # License 26 | 27 | MIT 28 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thunkify", 3 | "version": "2.1.2", 4 | "repo": "tj/node-thunkify", 5 | "description": "Turn callbacks, arrays, generators, generator functions, and promises into a thunk", 6 | "keywords": ["thunk", "co", "generator", "generators", "promise"], 7 | "main": "index.js", 8 | "scripts": ["index.js"], 9 | "dependencies": { 10 | "component/assert": "*" 11 | }, 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var assert = require('assert'); 7 | 8 | /** 9 | * Expose `thunkify()`. 10 | */ 11 | 12 | module.exports = thunkify; 13 | 14 | /** 15 | * Wrap a regular callback `fn` as a thunk. 16 | * 17 | * @param {Function} fn 18 | * @return {Function} 19 | * @api public 20 | */ 21 | 22 | function thunkify(fn){ 23 | assert('function' == typeof fn, 'function required'); 24 | 25 | return function(){ 26 | var args = new Array(arguments.length); 27 | var ctx = this; 28 | 29 | for(var i = 0; i < args.length; ++i) { 30 | args[i] = arguments[i]; 31 | } 32 | 33 | return function(done){ 34 | var called; 35 | 36 | args.push(function(){ 37 | if (called) return; 38 | called = true; 39 | done.apply(null, arguments); 40 | }); 41 | 42 | try { 43 | fn.apply(ctx, args); 44 | } catch (err) { 45 | done(err); 46 | } 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thunkify", 3 | "version": "2.1.2", 4 | "repository": "tj/node-thunkify", 5 | "description": "Turn callbacks, arrays, generators, generator functions, and promises into a thunk", 6 | "keywords": ["thunk", "co", "generator", "generators", "promise"], 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "mocha": "*", 10 | "should": "*" 11 | }, 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | var thunkify = require('..'); 3 | var assert = require('assert'); 4 | var fs = require('fs'); 5 | 6 | describe('thunkify(fn)', function(){ 7 | it('should work when sync', function(done){ 8 | function read(file, fn) { 9 | fn(null, 'file: ' + file); 10 | } 11 | 12 | read = thunkify(read); 13 | 14 | read('foo.txt')(function(err, res){ 15 | assert(!err); 16 | assert('file: foo.txt' == res); 17 | done(); 18 | }); 19 | }) 20 | 21 | it('should work when async', function(done){ 22 | function read(file, fn) { 23 | setTimeout(function(){ 24 | fn(null, 'file: ' + file); 25 | }, 5); 26 | } 27 | 28 | read = thunkify(read); 29 | 30 | read('foo.txt')(function(err, res){ 31 | assert(!err); 32 | assert('file: foo.txt' == res); 33 | done(); 34 | }); 35 | }) 36 | 37 | it('should maintain the receiver', function(done){ 38 | function load(fn) { 39 | fn(null, this.name); 40 | } 41 | 42 | var user = { 43 | name: 'tobi', 44 | load: thunkify(load) 45 | }; 46 | 47 | user.load()(function(err, name){ 48 | if (err) return done(err); 49 | assert('tobi' == name); 50 | done(); 51 | }); 52 | }) 53 | 54 | it('should catch errors', function(done){ 55 | function load(fn) { 56 | throw new Error('boom'); 57 | } 58 | 59 | load = thunkify(load); 60 | 61 | load()(function(err){ 62 | assert(err); 63 | assert('boom' == err.message); 64 | done(); 65 | }); 66 | }) 67 | 68 | it('should ignore multiple callbacks', function(done){ 69 | function load(fn) { 70 | fn(null, 1); 71 | fn(null, 2); 72 | fn(null, 3); 73 | } 74 | 75 | load = thunkify(load); 76 | 77 | load()(done); 78 | }) 79 | 80 | it('should pass all results', function(done){ 81 | function read(file, fn) { 82 | setTimeout(function(){ 83 | fn(null, file[0], file[1]); 84 | }, 5); 85 | } 86 | 87 | read = thunkify(read); 88 | 89 | read('foo.txt')(function(err, a, b){ 90 | assert(!err); 91 | assert('f' == a); 92 | assert('o' == b); 93 | done(); 94 | }); 95 | }) 96 | 97 | it('should work with node methods', function(done){ 98 | fs.readFile = thunkify(fs.readFile); 99 | 100 | fs.readFile('package.json')(function(err, buf){ 101 | assert(!err); 102 | assert(Buffer.isBuffer(buf)); 103 | 104 | fs.readFile('package.json', 'utf8')(function(err, str){ 105 | assert(!err); 106 | assert('string' == typeof str); 107 | done(); 108 | }); 109 | }); 110 | }) 111 | }) --------------------------------------------------------------------------------