I have been struggling a bit with an edge case of
fab.stream
—a (fab) app from the upcoming version 0.5 of fab.js. To explore that difficulty, I break down the problem into a very small (fab) app:with ( fab )As I found out last night, HTML (fab) apps that wrap content (like
with ( html )
( fab )
// Listen on the FAB port and establish the faye server
( listen, 0xFAB )
( PRE )
( callback_test )
() // /pre
();
<pre>
) do so by writing to the response stream, then reading from the upstream app response (e.g. callback_test
above), then writing the closing tag to the response stream. The end result being that callback_test
has upstream data in the response when it gets invoked.It makes perfect sense but ended up causing trouble in certain situations. Specifically, when the upstream app writes multiple times to the response stream, then the HTML app will send multiple close tags.
This does not occur for simple apps. For instance if I grab a string
foo
from a function and write it to the response stream, then write the hard-coded string "bar" to the response stream like so:function non_stream_test(write) {...It just works. Accessing this resource via
var foo = function_that_returns_foo();
return write(foo)
("bar");
}
curl
returns:cstrom@whitefall:~$ curl -N http://localhost:4011/The foo + bar test is wrapped in the
<pre>foobar</pre>
<pre>
tag as expected.Of course, I could also just as easily written to the response stream once like so:
function non_stream_test(write) {My problem is that I think the first way is just so cool.
var foo = function_that_returns_foo();
return write(foo + "bar");
}
Anyhow.
As I mentioned, the above cases just work. Problems arise when dealing will callbacks—for example, looking up a list of players in a database. Since I am using Javascript, the database lookup expects to be called with function that it can invoke once it has retrieved the list of player. This is Javascript, so it is quite common.
In that case, I cannot return a
write()
of data. I only have access to the data inside the callback. Calling return inside the callback will return from the callback, not the function that originally requested the data. Fortunately, in v0.5 of fabjs, there is fab.stream to handle this.Consider a silly callback that invokes the supplied function with a single argument, "foo":
function callback_with_foo(fn) { fn("foo") }I can then get that value, along with "bar" by doing something like:
function callback_test(write) {This is the situation in which I will get a second closing
return write(function(write) {
return fab.stream(function(stream) {
callback_with_foo(function (foo) {
stream(write(foo));
stream(write("bar"));
stream(write());
});
});
});
}
<pre>
tag:cstrom@whitefall:~$ curl -N http://localhost:4011/Each
<pre>foo</pre>bar</pre>
stream(write())
still has the closing tag in the upstream just waiting to be sent back to the browser. And merrily sent it is.For the past 3 nights, I have been banging my head against the proverbial wall trying to figure out a way around this. Today, it finally occurred to me that there is no need. I cannot think of a use case in which I would want to write twice in a callback and, even if I could, then it might warrant a bit more firepower than (fab) HTML apps. A perfectly cromulent solution is to simply accumulate the string data and then write it once:
function callback_test(write) {(the second
return write(function(write) {
return fab.stream(function(stream) {
callback_with_foo(function (foo) {
stream(write(foo + "bar"));
stream(write());
});
});
});
}
stream()
with an empty write()
signals to stream that it is done sending data)With that, I get my properly wrapped text from a callback:
cstrom@whitefall:~$ curl -N http://localhost:4011/Sometimes I can really obsess over very tiny, immaterial details. Such is my burden.
<pre>foobar</pre>
Tomorrow, I am definitely looking at something beside
fab.stream
!Day #229
No comments:
Post a Comment