Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 227 lines (151 sloc) 11.136 kB
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
1 ### I get a weird "Function contains async calls but does not have _ parameter" error. What's the deal?
fb4394e @bjouhier created FAQ.md
bjouhier authored
2
3 You'll get this error with code like:
4
5 ``` javascript
6 function foo() { bar(_); }
7 ```
8
9 Asynchronism is _contagious_.
10 Function `foo` contains an async call to `bar`.
11 So `foo` itself becomes asynchronous and needs an `_` in its parameter list. Change it to:
12
13 ``` javascript
14 function foo(_) { bar(_); }
15 ```
16
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
17 ### Can I create an anonymous streamline function?
fb4394e @bjouhier created FAQ.md
bjouhier authored
18
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
19 Yes:
fb4394e @bjouhier created FAQ.md
bjouhier authored
20
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
21 ``` javascript
22 function(_) { bar(_); }
23 ```
fb4394e @bjouhier created FAQ.md
bjouhier authored
24
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
25 ### What if my async function does not do any async calls?
fb4394e @bjouhier created FAQ.md
bjouhier authored
26
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
27 ``` javascript
28 function foo(_) { return bar(); }
29 ```
30
31 Nothing bad will happen. But you have to call `foo` as `foo(_)`. If you call it as `foo()` you'll get a _future_ back, probably not what you want.
fb4394e @bjouhier created FAQ.md
bjouhier authored
32
33 There will be a bit of overhead so you should avoid declaring sync functions with an `_`.
34
35 But you many need this feature, for example if you dispatch to functions that may be either async or sync.
36 In this case you should dispatch asynchronously (pass an `_`) and declare all of dispatch handlers as async functions, even those that are synchronous.
37
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
38 ### Why do the built-in streamline functions (`array.forEach_`, `map_`, etc.) have `_` as first parameter rather than last?
fb4394e @bjouhier created FAQ.md
bjouhier authored
39
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
40 Because it makes it easier to deal with optional parameters.
41
de7d1cd @bjouhier bumped version number before NPM publish
bjouhier authored
42 It also make it easier to check that the `_` is passed to every async call. You don't have to look at the end of the parameter list.
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
43
44 The standard node convention is to have the callback as last parameter.
45 You should follow this convention if you design libraries for a general node audience.
de7d1cd @bjouhier bumped version number before NPM publish
bjouhier authored
46 But you may choose to pass the callback as first parameter if you design private APIs or if you target a more restricted audience of streamliners.
fb4394e @bjouhier created FAQ.md
bjouhier authored
47
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
48 ### Can a streamline function take optional parameters?
fb4394e @bjouhier created FAQ.md
bjouhier authored
49
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
50 Yes but you have to be careful with the special `arguments` variable. You cannot pass it blindly to another call with `apply`. You have to use the streamline `apply_` built-in function instead. See its documentation.
fb4394e @bjouhier created FAQ.md
bjouhier authored
51
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
52 ### It does not work and I'm not even getting an exception. What's going on?
fb4394e @bjouhier created FAQ.md
bjouhier authored
53
8cfaa54 @bjouhier fixed #82
bjouhier authored
54 You probably called a buggy asynchronous function and you did not pass `_`. For example:
fb4394e @bjouhier created FAQ.md
bjouhier authored
55
56 ``` javascript
57 function buggy(_) { undefined.toString(); }
58 buggy();
59 ```
60
61 The problem is that when you call `buggy()` without `_` it returns a _future_. The future memorizes the exception but does not throw it. Try the following:
62
63 ``` javascript
64 function buggy(_) { undefined.toString(); }
65 var f = buggy();
66 console.log("after buggy()"); // you'll see this one
67 f(_); // throws the exception
68 console.log("after f(_)"); // you won't see this one
69 ```
70
7ddb9e7 @bjouhier fixed #112 (path.exists -> fs.exists)
bjouhier authored
71 ### I'm calling `fs.exists(fname, _)` and it does not work. What am I doing wrong?
fb4394e @bjouhier created FAQ.md
bjouhier authored
72
73 Streamline is designed to work with functions that take standard node callbacks, i.e. callbacks that have the standard `cb(err[, result])` signature.
74
7ddb9e7 @bjouhier fixed #112 (path.exists -> fs.exists)
bjouhier authored
75 Unfortunately some libraries use a different callback signature, and node itself has a few exceptions, the most notable one being `fs.exists` which does not have any `err` parameter in its callback.
fb4394e @bjouhier created FAQ.md
bjouhier authored
76
77 The workaround is to write a small wrapper that re-aligns the callback parameters on the standard:
78
79 ``` javascript
80 function exists(fname, cb) {
7ddb9e7 @bjouhier fixed #112 (path.exists -> fs.exists)
bjouhier authored
81 fs.exists(fname, function(result) { cb(null, result); });
fb4394e @bjouhier created FAQ.md
bjouhier authored
82 }
83
84 console.log(__filename + ': ' + exists(__filename, _)); // works
7ddb9e7 @bjouhier fixed #112 (path.exists -> fs.exists)
bjouhier authored
85 console.log(__filename + ': ' + fs.exists(__filename, _)); // does not work
fb4394e @bjouhier created FAQ.md
bjouhier authored
86 ```
87
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
88 ### I'm calling an async function without `_` and I'm not getting a future back. What's wrong?
fb4394e @bjouhier created FAQ.md
bjouhier authored
89
90 You're calling a function which was not written with streamline, for example one of node's `fs` function. The workaround is easy: just wrap it with a streamline function:
91
92 ``` javascript
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
93 // the wrappers
fb4394e @bjouhier created FAQ.md
bjouhier authored
94 function readTextFile(path, enc, _) { return fs.readFile(path, enc, _); }
95 function readBinaryFile(path, _) { return fs.readFile(path, _); }
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
96
97 // testing futures
98 var f1 = readTextFile(path1, "utf8"); // ok
99 var f2 = fs.readFile(path2, "utf8"); // does not fail but f2 is undefined
100 var data1 = f1(_); // ok: f1 is a future
101 var data2 = f2(_); // fails!
fb4394e @bjouhier created FAQ.md
bjouhier authored
102 ```
103
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
104 Wrapping functions with optional arguments might be a bit tricky. The following wrapper will work though:
fb4394e @bjouhier created FAQ.md
bjouhier authored
105
106 ``` javascript
107 function readFile(path, enc, _) {
108 if (typeof enc === 'string') return fs.readFile(path, enc, _);
109 else return fs.readFile(path, _);
110 }
111 ```
112
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
113 ### My flow control is completely broken. I am completely lost. Help!
114
115 Check your file extensions.
116
117 If you put streamline code in a `.js` or `.coffee` file it won't get transformed
118 and your code will go wild: callbacks will be executed multiple times, etc.
119
120 ### Can I use the streamline extensions for files that don't contain any async code (yet).
121
122 This won't hurt. Streamline only transforms functions that have the special `_` parameter.
123 So, files that don't contain async code won't be impacted by the transformation.
124
de7d1cd @bjouhier bumped version number before NPM publish
bjouhier authored
125 The only drawback is a slower application startup because more files get transformed but you can avoid that with the `--cache` option.
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
126
b438550 @bjouhier close #94 (streamline + express)
bjouhier authored
127 ### Can I use streamline.js with the _express_ middleware
128
129 Yes! Take a look at [express-streamline](https://github.com/aseemk/express-streamline).
130
131 And read just below about dealing with events!
132
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
133 ### The underscore trick is designed for callbacks but not events. How do I deal with events?
fb4394e @bjouhier created FAQ.md
bjouhier authored
134
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
135 If you are dealing with stream events, you should try streamline's stream API. It wraps node streams with a simple callback oriented API and it takes care of the low level event handling for you (`pause/resume` on readable streams, `drain` on writable streams). For example:
fb4394e @bjouhier created FAQ.md
bjouhier authored
136
137 ``` javascript
138 var streams = require('streamline/lib/streams');
139
140 var inStream = new streams.ReadableStream(nodeInStream);
141 var head = inStream.read(_, 128); // read the first 128 bytes
142 var chunk;
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
143 while (chunk = inStream.read(_)) {
144 // do something with chunk
145 }
fb4394e @bjouhier created FAQ.md
bjouhier authored
146
147 var outStream = new streams.WritableStream(nodeOutStream);
148 outStream.write(_, result);
149 ```
150
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
151 This module also contains wrappers around node's `Http` and `Net` objects, both client and server.
fb4394e @bjouhier created FAQ.md
bjouhier authored
152 See the `streams` documentation for details.
153
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
154 If you are not dealing with stream events, you can take a look at the implementation of the streams module for ideas. Any event API can be turned into a callback API (with a `getEvent(_)` call that you would call in a loop) but this can be totally counterprodutive (events will be serialized).
155
156 If the events are loosely correlated, it is better to let them be dispatched as events. But in this case, you may want to use streamline to handle the logic of each event you subscribed to. This is not too difficult: just put a small anonymous function wrapper inside your event handlers:
fb4394e @bjouhier created FAQ.md
bjouhier authored
157
158 ``` javascript
159 function handleError(err) {
160 // log it somewhere
161 }
162
163 server.on('error', handleError);
164 server.on('eventA', function(arg) {
165 (function(_) {
166 // function has an _ parameter, you can use streamline
167 })(handleError);
168 });
169 server.on('eventB', function(arg) {
170 (function(_) {
171 // streamline code...
172 })(handleError);
173 });
174 ```
175
176 If the event handler takes a fixed number of parameters, there is even a lighter solution: just add an `_` parameter to each event handler:
177
178 ``` javascript
179 server.on('eventA', function(arg, _) {
180 // function has an _ parameter, you can use streamline
181 }).on('eventB', function(arg, _) {
182 // streamline code
183 });
184 ```
185
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
186 It works because `function(arg, _)` will be called as a _future_ (without `_`). But it is a bit more fragile because errors are not trapped and code would break if the server changes and starts passing more arguments to its event handlers.
fb4394e @bjouhier created FAQ.md
bjouhier authored
187
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
188 ### Are there limitations? Am I limited to a subset of Javascript?
fb4394e @bjouhier created FAQ.md
bjouhier authored
189
190 Hardly any. Streamline knows how to transform all Javascript constructs except two:
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
191
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
192 * labelled `break` and `continue`.
193 * non-empty switch `case` that falls into another `case` without `break` nor `return`.
fb4394e @bjouhier created FAQ.md
bjouhier authored
194
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
195 The transformation engine could be improved to handle these constructs too but they are rather hairy and workarounds are easy so they haven't been implemented yet. You'll get a compilation error if you use these constructs.
fb4394e @bjouhier created FAQ.md
bjouhier authored
196
197 On the other hand, you can do all sorts of crazy things, like calling async functions from object or array literals, or even writing async constructors. The following will work:
198
199 ``` javascript
200 var foo = [f1(_), f2(_), f3(_)].filter_(_, function(_, elt) { return elt.g1(_) || elt.g2(_); });
201
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
202 function Bar(_, name) { this.name = name; baz(_); }
fb4394e @bjouhier created FAQ.md
bjouhier authored
203 var bar = new Bar(_, "zoo");
204 ```
205
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
206 ### Will I always get the same semantics as in normal (sync) Javascript?
fb4394e @bjouhier created FAQ.md
bjouhier authored
207
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
208 The streamline compiler works by applying patterns. These patterns have been carefully crafted to preserve semantics. The only known case where streamline may diverge is the order of evaluation of subexpressions inside a given statement.
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
209
210 In callbacks mode, streamline evaluates the asynchronous subexpressions before the synchronous ones. So if you have `foo() + bar(_)`, it will evaluate `bar(_)` before `foo()`.
211
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
212 In fibers mode, streamline preserves the order and evaluates `foo()` first. So you should not write _fragile_ code that relies on precise order of evaluation of subexpressions.
fb4394e @bjouhier created FAQ.md
bjouhier authored
213
214 But streamline guarantees the ordering in the cases where it really matters: logical operators (`&&` and `||`), ternary operator (`cond ? a : b`) and comma operator (`a, b, c`). If you write `foo() && bar(_)`, `foo()` will be evaluated first and `bar(_)` will only be evaluated if `foo()` is true.
215
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
216 ### What about performance? Am I taking a hit?
fb4394e @bjouhier created FAQ.md
bjouhier authored
217
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
218 In callback mode, streamline generates callbacks that are very similar to the ones you would be writing by hand. So you are only paying a small overhead. Usually, the overhead will be small in comparison to the time spent in the async functions that you are calling. For example, you incur a 50% overhead when calling `process.nextTick(_)`, which is the fastest async call in node.js. If you call `setTimeout(_, 0)` the overhead drops to 18%. And on a real (but simple) I/O call like `fs.stat` it goes down to 3 or 4%.
fb4394e @bjouhier created FAQ.md
bjouhier authored
219
81c0b7c @bjouhier cleanup pass on FAQ.md
bjouhier authored
220 The fibers mode has more overhead on I/O calls but it eliminates all the callback overhead in the layers that call low level I/O services. So depending on the thickness of the logic that sits on top of the I/O layers you may get an increase or decrease of performance. The nice thing is that you don't need to choose between callbacks and fibers upfront. You can write your code, compare performance and then choose the best mode for deployment.
fb4394e @bjouhier created FAQ.md
bjouhier authored
221
bad5439 @bjouhier cleanup pass on FAQ.md
bjouhier authored
222 Some patterns like caching can give surprising results (see https://gist.github.com/2362015).
fb4394e @bjouhier created FAQ.md
bjouhier authored
223
cc1bea2 @bjouhier first pass on FAQ.md
bjouhier authored
224 There is also room for improvement. In callback mode the small overhead comes from the additional comfort and security that streamline gives you: sync stack traces, global context, trampoline, rigorous exception handling. This could be improved with options that disable these _comfort_ features but it would make the tool more complex.
fb4394e @bjouhier created FAQ.md
bjouhier authored
225
226 Future versions of V8 will likely support harmony generators. Streamline could then provide a third transformation mode that takes advantage of this language feature and may benefit from additional V8 optimizations. But all this is still speculative at this stage.
Something went wrong with that request. Please try again.