0
# Thunkify
1
2
Thunkify converts regular Node.js callback-based functions into thunks, which are functions that accept a callback as their only parameter. This transformation is particularly useful for generator-based flow control systems like co, enabling seamless integration of callback-based APIs with modern async/await patterns.
3
4
## Package Information
5
6
- **Package Name**: thunkify
7
- **Package Type**: npm
8
- **Language**: JavaScript
9
- **Installation**: `npm install thunkify`
10
- **License**: MIT
11
- **Dependencies**: None
12
13
## Core Imports
14
15
```javascript
16
const thunkify = require('thunkify');
17
```
18
19
For ES modules:
20
21
```javascript
22
import thunkify from 'thunkify';
23
```
24
25
## Basic Usage
26
27
```javascript
28
const thunkify = require('thunkify');
29
const fs = require('fs');
30
31
// Convert fs.readFile to a thunk
32
const readFile = thunkify(fs.readFile);
33
34
// Use the thunked function
35
readFile('package.json', 'utf8')(function(err, data) {
36
if (err) throw err;
37
console.log(data);
38
});
39
40
// Works with generator-based flow control
41
function* readFiles() {
42
const data1 = yield readFile('file1.txt', 'utf8');
43
const data2 = yield readFile('file2.txt', 'utf8');
44
return [data1, data2];
45
}
46
```
47
48
## Capabilities
49
50
### Function Conversion
51
52
Converts callback-based functions into thunks for use with generator-based flow control.
53
54
```javascript { .api }
55
/**
56
* Wrap a regular callback function as a thunk
57
* @param {Function} fn - A callback-based function to convert
58
* @returns {Function} A thunk function that accepts arguments and returns a function expecting a callback
59
* @throws {AssertionError} If fn is not a function
60
*/
61
function thunkify(fn): (...args: any[]) => (callback: (err?: Error, ...results: any[]) => void) => void;
62
```
63
64
The `thunkify` function:
65
66
- **Input**: Takes any callback-based function that follows Node.js callback conventions (error-first callback)
67
- **Output**: Returns a thunk function that preserves all original arguments and context
68
- **Context Preservation**: Maintains `this` context when used with object methods
69
- **Error Handling**: Catches synchronous exceptions and forwards them to the callback
70
- **Callback Safety**: Ensures the callback is only called once, ignoring subsequent calls
71
- **Argument Forwarding**: Preserves all function arguments and callback results
72
73
**Usage Examples:**
74
75
```javascript
76
const thunkify = require('thunkify');
77
const fs = require('fs');
78
79
// Basic function thunking
80
const readFile = thunkify(fs.readFile);
81
readFile('package.json', 'utf8')(function(err, data) {
82
console.log(data);
83
});
84
85
// Method thunking with context preservation
86
const user = {
87
name: 'Alice',
88
load: thunkify(function(callback) {
89
// 'this' refers to the user object
90
callback(null, this.name);
91
})
92
};
93
94
user.load()(function(err, name) {
95
console.log(name); // 'Alice'
96
});
97
98
// Error handling - synchronous errors are caught
99
const faultyFn = thunkify(function(callback) {
100
throw new Error('Something went wrong');
101
});
102
103
faultyFn()(function(err) {
104
console.log(err.message); // 'Something went wrong'
105
});
106
107
// Multiple return values are preserved
108
const multiReturn = thunkify(function(a, b, callback) {
109
callback(null, a + b, a * b);
110
});
111
112
multiReturn(5, 3)(function(err, sum, product) {
113
console.log(sum); // 8
114
console.log(product); // 15
115
});
116
```
117
118
## Error Handling
119
120
Thunkify provides comprehensive error handling:
121
122
- **Input Validation**: Throws `AssertionError` if the input is not a function
123
- **Synchronous Errors**: Catches thrown exceptions and passes them to the callback
124
- **Callback Protection**: Prevents multiple callback invocations, ignoring subsequent calls
125
- **Error Forwarding**: Preserves the original error-first callback pattern
126
127
```javascript
128
const thunkify = require('thunkify');
129
130
// Input validation error
131
try {
132
thunkify('not a function');
133
} catch (err) {
134
console.log(err.name); // 'AssertionError'
135
}
136
137
// Synchronous error handling
138
const errorFn = thunkify(function(callback) {
139
throw new Error('Sync error');
140
});
141
142
errorFn()(function(err) {
143
console.log(err.message); // 'Sync error'
144
});
145
```
146
147
## Integration Patterns
148
149
### Generator-based Flow Control
150
151
Thunkify is designed to work seamlessly with generator-based libraries like co:
152
153
```javascript
154
const co = require('co');
155
const thunkify = require('thunkify');
156
const fs = require('fs');
157
158
const readFile = thunkify(fs.readFile);
159
const writeFile = thunkify(fs.writeFile);
160
161
co(function* () {
162
const data = yield readFile('input.txt', 'utf8');
163
const processed = data.toUpperCase();
164
yield writeFile('output.txt', processed, 'utf8');
165
console.log('File processing complete');
166
}).catch(console.error);
167
```
168
169
### Async/Await Compatibility
170
171
While primarily designed for generators, thunked functions can be adapted for async/await:
172
173
```javascript
174
const thunkify = require('thunkify');
175
const { promisify } = require('util');
176
177
// Convert thunk to promise
178
function thunkToPromise(thunk) {
179
return new Promise((resolve, reject) => {
180
thunk((err, ...args) => {
181
if (err) reject(err);
182
else resolve(args.length === 1 ? args[0] : args);
183
});
184
});
185
}
186
187
// Usage with async/await
188
async function example() {
189
const readFile = thunkify(fs.readFile);
190
const data = await thunkToPromise(readFile('package.json', 'utf8'));
191
console.log(data);
192
}
193
```