Parsing file uploads at 500 mb/s with node.js
debuggable.com
debuggable.com
</pedantry>
http://nodejs.org/jsconf2010.pdf
> Coroutines complicate the mental model while adding only cheap syntactic pleasures.
I suppose someone who wants them in node.js can just reimplement them as an extension.
Basically callbacks and coroutines don't play well together.
But there were a bunch of reasons for the removal, performance and quality of the initial implementation also played into it AFAIK.
Can you elaborate on this? I thought the opposite: coroutines play very well with callbacks. Callback libraries force you to write programs in continuation passing style. Coroutines let you write asynchronous programs in normal style. Lets assume we have an asynchronous readAsync(callback) function that reads a number and calls the callback with the input when it's ready. Now you're writing your program like this:
readAsync(function(x){
readAsync(function(y){
write(x+y)
})
})
What you want to write is this: x = read() // may block this coroutine, but *not* the entire program
y = read()
write(x+y)
This can be achieved with something like: function read(){
coro = getCurrentCoroutine()
var result
callback = function(x){ result = x; coro.resume() }
readAsync(callback)
yield // stops the current coroutine until it is resumed
return result
}
It seems to me that this is a general way to adapt any callback style library to coroutines.But then again, Erlang is probably a language more "suitable" to high concurrency programming, but node's goal is to make writing scalable networks programs possible for everyone. One might call it the PHP of concurrency : )
The problem is just that coroutines are not natural in JS, and people will shoot themselves in the foot all the time.
Some "features" are better left out to allow a bigger audience to write reliable Software. Your milage may vary.
readAsync(function(x) {
readAsync(function(y) {
write(x+y)
})
})
vs x = read()
y = read()
write(x+y)
You don't really need to know how to use the full power of coroutines if you just want asynchronous operations. You just need to know that read() may block the current coroutine. future_x = read();
x = future_x.wait();
y = read().wait(); // shortcut
write(x + y);
Or something like this if the reads are independent: future_x = read();
future_y = read();
// one of a handful of functions that can "block", all operating on futures:
waitForAll(future_x, future_y);
write(future_x.get() + future_y.get());
Using futures rather than implicit suspension has the added advantage of being able to pipeline independent reads just as you can with callback-style asynchronous I/O.You can already implement[1] an approximation of this in terms of callbacks, but it doesn't look quite as nice, e.g.:
var handler = new AsyncHandler();
// independent, pipelined reads
readAsync(handler.cb());
readAsync(handler.cb());
handler.whenDone(function(x, y) {
write(x+y);
});
It gets substantially uglier than that if the dependencies aren't so straightforward, e.g. A, B & C are independent, D depends on A & B having completed and the last part of the code requires the results from C & D. Futures do much better in that sort of situation.[1] I've built a basic but usable helper for this purpose: http://github.com/pmj/MultiAsync-js/tree/master/src/ I hear the Dojo toolkit contains something similar.
Why do you have a separate wait & waitForAll? Couldn't get() wait automatically?
get() vs. wait() is admittedly a question of preference. Personally, I'd keep them separate and even go so far as to have it warn you if you called get() without a wait() or a successful isReady() or so on that future. It keeps the suspensions explicit and the intentions clear. It's a bit like explicit vs implicit transactional systems.
Func<string> reader = read;
IAsyncResult future_x = reader.BeginInvoke(null, null)
IAsyncResult future_y = reader.BeginInvoke(null, null)
WaitHandle.WaitAll(new[] { future_x.AsyncWaitHandle, future_y.AsyncWaitHandle }); // takes optional timeout
string x = reader.EndInvoke(future_x);
string y = reader.EndInvoke(future_y);
write(x + y);But afaik, that's pretty much never the case with file uploads, unless you are uploading a file that is still growing in size - so yeah, it's annoying : ).
('felixge2' because my other account is in noprocrast mode: )
If a connection is closed, either by a timeout, or EOF, you simply check if the promised content length matches the count of received bytes - if not you should probably discard the whole thing (unless you're specifically supporting broken clients).