All the code and such a big abstraction for the first example when it could have done like this: var result = []; paths.forEach(function (i, file){ fs.stat(file, function (err, data){ result.push(data); if (i === 0) { // Use stat size } if (result.length === paths.length) { // Use the stats } }); }); Fairly understandable, more efficient and without introducing logic patterns foreign to many. It also meet his require…
Callbacks are imperative, promises are functional
11–20 of 154 posts
Re: Callbacks are imperative, promises are functional
#12If you have some crazy graph of dependencies, I can see how breaking out promises could help simplify things. But I don't feel like that's a super-common scenario.
The author says:
> * [Promises] are easier to think about precisely because we’ve delegated part of our thought process to the machine. When using the async module, our thought process is:*
> A. The tasks in this program depend on each other like so,
> B. Therefore the operations must be ordered like so,
> C. Therefore let’s write code to express B.
> Using graphs of dependent promises lets you skip step B altogether.
But in most cases, I don't want to skip B. As a programmer, I generally find myself preferring to know what order things are happening in. At most, I'll parallelize a few of database calls or RPC's, but it's never that complex. (And normal async-helper libraries work just fine.)
I swear I want to wrap my head around how this promises stuff could be useful in everyday, "normal" webserver programming, but it just always feels like over-abstraction to me, obfuscating what the code is actually doing, hindering more than helping. I want to know, specifically, if one query is running before another, or after another, or in parallel -- web programming is almost entirely about side effects, at least in my experience, so these things often matter an awful lot.
I'm still waiting for a real-world example of where promises help with the kind of everyday webserver (or client) programming which the vast majority of programmers actually do.
> Getting the result out of a callback- or event-based function basically means “being in the right place at the right time”. If you bind your event listener after the result event has been fired, or you don’t have code in the right place in a callback, then tough luck, you missed the result. This sort of thing plagues people writing HTTP servers in Node. If you don’t get your control flow right, your program breaks.
I have literally never had this problem. I don't think it really plagues people writing HTTP servers. I mean, you really don't know what you're doing if you try to bind your event listener after a callback has fired. Remember, callbacks only ever fire AFTER your current imperative code has finished executing, and you've returned control to node.js.
Re: Callbacks are imperative, promises are functional
#13Re: Callbacks are imperative, promises are functional
#14All the code and such a big abstraction for the first example when it could have done like this: var result = []; paths.forEach(function (i, file){ fs.stat(file, function (err, data){ result.push(data); if (i === 0) { // Use stat size } if (result.length === paths.length) { // Use the stats } }); }); Fairly understandable, more efficient and without introducing logic patterns foreign to many. It also meet his require…
Re: Callbacks are imperative, promises are functional
#15I feel this is twisting the meaning of functional programming. Excel is not functional. It is declarative. You declare the relationships between the cells and Excel uses those to propagate changes. Just like a makefile is not functional but declarative. The dependency of the relationships are enforced to produce action. SQL is another example of declarative language and it is nowhere near as functional.
It's not functional at all. This reaffirms my belief that blog posts are a terrible place to learn. People who know the least shout the loudest.
Re: Callbacks are imperative, promises are functional
#16All the code and such a big abstraction for the first example when it could have done like this: var result = []; paths.forEach(function (i, file){ fs.stat(file, function (err, data){ result.push(data); if (i === 0) { // Use stat size } if (result.length === paths.length) { // Use the stats } }); }); Fairly understandable, more efficient and without introducing logic patterns foreign to many. It also meet his require…
The real problem, design-wise, is that fs.stat operates on a single file at a time. Sometimes you only want info on one file, sure, but in many common use cases, you want info on a bunch of files - perhaps even the contents of an entire directory, or a directory tree. Worse still, stat might be a syscall! Woo, syscall per file.
And in such case you would only do this once outside the listeners of http/or-whatever connections so it would be done just once regardless of the number of concurrent activity.
Re: Callbacks are imperative, promises are functional
#17All the code and such a big abstraction for the first example when it could have done like this: var result = []; paths.forEach(function (i, file){ fs.stat(file, function (err, data){ result.push(data); if (i === 0) { // Use stat size } if (result.length === paths.length) { // Use the stats } }); }); Fairly understandable, more efficient and without introducing logic patterns foreign to many. It also meet his require…
var result = [];
var hasFailed = false;
paths.forEach(function (i, file){
fs.stat(file, function (err, data){
// if previous callback failed, give up.
// unless this is the first item, which we still need
if(hasFailed && i !== 0) return;
if(err) {
hasFailed = true;
// do something with the error
if (i === 0) // do something special for error on first item.
return;
}
result.push(data);
if (i === 0) {
// Use stat size
// remember we might have already failed, in which case don't add the first item to the general result
if(hasFailed) return;
}
if (result.length === paths.length) {
// Use the stats
}
});
});
That's almost certainly still not close to right. Which just illustrates the basic problem: without either promises or something like async.js, you're reimplementing control flow by yourself. You can easily start with perfectly-nice-looking code that balloons to be incomprehensible as soon as you start caring about error cases. And where two statements, perhaps dozens of lines apart, are preserving some invariant that is not obvious to someone editing your code in the future. Even yourself.Re: Callbacks are imperative, promises are functional
#18Earlier quoted context omitted.
The real problem, design-wise, is that fs.stat operates on a single file at a time. Sometimes you only want info on one file, sure, but in many common use cases, you want info on a bunch of files - perhaps even the contents of an entire directory, or a directory tree. Worse still, stat might be a syscall! Woo, syscall per file.
... and what does that haves to do with promises? And in such case you would only do this once outside the listeners of http/or-whatever connections so it would be done just once regardless of the number of concurrent activity.
fs.statMany(filenames, function (stats) { ... });
or: var statsPromise = fs.statMany(filenames);
And then in either case, you'd just use a for-loop or forEach or whatever your preference on the result. No thinking about how to preserve complex invariants or whatever is necessary.Hell, with ES6 generator-y expressions you could make it even more succinct, something like:
var result;
fs.statMany(filenames, function (stats) { result = [... for x in stats]; });
No push nonsense, no nested if statements, no need to explicitly invoke async.parallel or whatever. Just clarity.Re: Callbacks are imperative, promises are functional
#19I feel this is twisting the meaning of functional programming. Excel is not functional. It is declarative. You declare the relationships between the cells and Excel uses those to propagate changes. Just like a makefile is not functional but declarative. The dependency of the relationships are enforced to produce action. SQL is another example of declarative language and it is nowhere near as functional.
Whereas in functional languages, the functions declare relationships between values and the language uses the evaluation model to propagate the results between function evaluation. Where's the difference?
Re: Callbacks are imperative, promises are functional
#20All the code and such a big abstraction for the first example when it could have done like this: var result = []; paths.forEach(function (i, file){ fs.stat(file, function (err, data){ result.push(data); if (i === 0) { // Use stat size } if (result.length === paths.length) { // Use the stats } }); }); Fairly understandable, more efficient and without introducing logic patterns foreign to many. It also meet his require…
Consider your use of the forEach() abstraction when you could have used a for() loop just as easily. (That said, I agree the article in general could do a better job of describing the "other" side)