Skip to content
This repository
Browse code

created FAQ.md

  • Loading branch information...
commit fb4394eeca9402ca04d5acf6790dac18fd582285 1 parent 17d4ee8
Bruno Jouhier authored April 18, 2012
183  FAQ.md
Source Rendered
... ...
@@ -0,0 +1,183 @@
  1
+## I get a weird "Function contains async calls but does not have _ parameter" error. What's the deal?
  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
+
  17
+## Can I create an anonymous streamline function?
  18
+
  19
+Yes: `function(_) { bar(_); }`
  20
+
  21
+## What if my async function does not do any async calls?
  22
+
  23
+For example: `function foo(_) { return bar(); }`
  24
+
  25
+Nothing bad will happen. But you have to call `foo` as `foo(_)`. If you call it as `foo()` you'll get a _future_ back, not the value returned by `bar()`.
  26
+
  27
+There will be a bit of overhead so you should avoid declaring sync functions with an `_`.  
  28
+
  29
+But you many need this feature, for example if you dispatch to functions that may be either async or sync.
  30
+In this case you should dispatch asynchronously (pass an `_`) and declare all of dispatch handlers as async functions, even those that are synchronous.
  31
+
  32
+## Why do the built-in streamline functions (`array.forEach_`, `map_`, etc.) have the `_` as first parameter rather than last?
  33
+
  34
+Because it makes it easier to deal with optional parameters.
  35
+
  36
+## Can a streamline function take optional parameters?
  37
+
  38
+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 `flows.apply` helper function. See its documentation.
  39
+
  40
+## It does not work and I'm not even getting an exception. What's going on?
  41
+
  42
+You probably called a buggy asynchronous functions and you did not pass `_`. For example:
  43
+
  44
+``` javascript
  45
+function buggy(_) { undefined.toString(); }
  46
+buggy();
  47
+```
  48
+
  49
+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:
  50
+
  51
+``` javascript
  52
+function buggy(_) { undefined.toString(); }
  53
+var f = buggy();
  54
+console.log("after buggy()"); // you'll see this one
  55
+f(_); // throws the exception
  56
+console.log("after f(_)"); // you won't see this one
  57
+```
  58
+
  59
+## I'm calling `path.exists(fname, _)` and it does not work. What am I doing wrong?
  60
+
  61
+Streamline is designed to work with functions that take standard node callbacks, i.e. callbacks that have the standard `cb(err[, result])` signature.
  62
+
  63
+Unfortunately some libraries use a different callback signature, and node itself has a few exceptions, the most notable one being `path.exists` which does not have any `err` parameter in its callback.
  64
+
  65
+The workaround is to write a small wrapper that re-aligns the callback parameters on the standard:
  66
+
  67
+``` javascript
  68
+function exists(fname, cb) { 
  69
+  path.exists(fname, function(result) { cb(null, result); });
  70
+}
  71
+
  72
+console.log(__filename + ': ' + exists(__filename, _)); // works
  73
+console.log(__filename + ': ' + path.exists(__filename, _)); // does not work
  74
+```
  75
+
  76
+## I'm calling an async function without `_` and I'm not getting a future back. What's wrong?
  77
+
  78
+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:
  79
+
  80
+``` javascript
  81
+function readTextFile(path, enc, _) { return fs.readFile(path, enc, _); }
  82
+function readBinaryFile(path, _) { return fs.readFile(path, _); }
  83
+```
  84
+
  85
+Wrapping functions with optional arguments might be a bit tricky. The following will work though:
  86
+
  87
+``` javascript
  88
+function readFile(path, enc, _) {
  89
+	if (typeof enc === 'string') return fs.readFile(path, enc, _);
  90
+	else return fs.readFile(path, _);
  91
+}
  92
+```
  93
+
  94
+## The underscore trick does not work with events. What can I do?
  95
+
  96
+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:
  97
+
  98
+``` javascript
  99
+var streams = require('streamline/lib/streams');
  100
+
  101
+var inStream = new streams.ReadableStream(nodeInStream);
  102
+var head = inStream.read(_, 128); // read the first 128 bytes
  103
+var chunk;
  104
+while (chunk = inStream.read(_)) { /* do something wiht chunk */ }
  105
+
  106
+var outStream = new streams.WritableStream(nodeOutStream);
  107
+outStream.write(_, result);
  108
+``` 
  109
+
  110
+There are also wrappers around `HTTP` and `Net` (TCP) objects, both client and server.
  111
+See the `streams` documentation for details.
  112
+
  113
+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 counterprodutive. If the events have very 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 use a small anonymous function wrapper:
  114
+
  115
+``` javascript
  116
+function handleError(err) {
  117
+	// log it somewhere
  118
+}
  119
+
  120
+server.on('error', handleError);
  121
+server.on('eventA', function(arg) {
  122
+    (function(_) {
  123
+		// function has an _ parameter, you can use streamline
  124
+    })(handleError);
  125
+});
  126
+server.on('eventB', function(arg) {
  127
+    (function(_) {
  128
+		// streamline code...
  129
+    })(handleError);	
  130
+});
  131
+```
  132
+
  133
+If the event handler takes a fixed number of parameters, there is even a lighter solution: just add an `_` parameter to each event handler:
  134
+
  135
+``` javascript
  136
+server.on('eventA', function(arg, _) {
  137
+	// function has an _ parameter, you can use streamline
  138
+}).on('eventB', function(arg, _) {
  139
+	// streamline code
  140
+});
  141
+```
  142
+
  143
+It works because `function(con, _)` 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.
  144
+
  145
+## Are there limitations? Am I limited to a subset of Javascript?
  146
+
  147
+Hardly any. Streamline knows how to transform all Javascript constructs except two:
  148
+* labelled break and continue. 
  149
+* non-empty switch case that falls into another switch case without a break or return.
  150
+
  151
+The transformation engine could be improved to handle these cases 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.
  152
+
  153
+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:
  154
+
  155
+``` javascript
  156
+var foo = [f1(_), f2(_), f3(_)].filter_(_, function(_, elt) { return elt.g1(_) || elt.g2(_); });
  157
+
  158
+function Bar(_, name) { this.name = name; }
  159
+var bar = new Bar(_, "zoo");
  160
+```
  161
+
  162
+## Will I always get the same semantics as in normal (sync) Javascript?
  163
+
  164
+The streamline compiler works by applying patters. These patterns have been carefully crafted to preserve semantics. The only known case where streamline may diverge is the order of evaluation of subexpressions in a given statement. In callbacks mode, streamline evaluates the asynchronous subexpressions before the synchronous ones. So if you have `foo() + bar(_)`, it will evaluate `bar(_)` before `foo()`.  In fibers mode, streamline preserves the order and evaluates `foo()` first. So you should not write _fragile_ code that relies on precise evaluation of subexpressions.
  165
+
  166
+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.
  167
+
  168
+## What about performance? Am I taking a big hit?
  169
+
  170
+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%.
  171
+
  172
+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 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, bench it in both modes and then choose the best one for deployment.
  173
+
  174
+Some patterns like caching can give surprising results (see ). The fibers mode can beat even the most optimized manually written callback code and the callback mode uses a trampoline technique which is faster than the `nextTick` pattern which is routinely used to avoid stack overflows.
  175
+
  176
+## Could performance be improved?
  177
+
  178
+Yes. 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 this would make the tool more complex.
  179
+
  180
+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.
  181
+
  182
+
  183
+
3  README.md
Source Rendered
@@ -263,8 +263,7 @@ try {
263 263
   setTimeout(_, 1000); // n1 fails, exception is memorized
264 264
   return n1(_) - n2(_); // exception is thrown by n1(_) expression.
265 265
 } catch (ex) {
266  
-  // execution will be caught here
267  
-  console.error(ex.stack);
  266
+  console.error(ex.stack); // exception caught here
268 267
 }
269 268
 ```
270 269
 
2  test/common/.callbacks/stack-test.js
@@ -2,7 +2,7 @@
2 2
 
3 3
 
4 4
 
5  
-var flows = require("streamline/lib/util/flows");
  5
+
6 6
 
7 7
 
8 8
 function nextTick(cb) {
2  test/common/stack-test._js
@@ -2,7 +2,7 @@
2 2
 // Line numbers matter to this test!
3 3
 
4 4
 var module = QUnit.module;
5  
-var flows = require("streamline/lib/util/flows");
  5
+
6 6
 
7 7
 
8 8
 function nextTick(cb){

0 notes on commit fb4394e

Please sign in to comment.
Something went wrong with that request. Please try again.