Live data from Hacker News

Callbacks are imperative, promises are functional

blog.jcoglan.com

11–20 of 154 posts

Re: Callbacks are imperative, promises are functional

#11

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…

[deleted]

Re: Callbacks are imperative, promises are functional

#12
This is an interesting perspective. But to me, even having spent a year on a large node.js project, I just don't see how promises would have simplified things at all.

If 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

#13
I 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.

Re: Callbacks are imperative, promises are functional

#14

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…

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.

Re: Callbacks are imperative, promises are functional

#15
post #13

I 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.

This was my thought as well. Promises are declarative... making a promise is almost the very definition of declarative programming.

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

#16

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…

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.

Re: Callbacks are imperative, promises are functional

#17

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…

To be fair, you haven't handled the complete case. What if one of the items fails? You need to handle the error, but only if it's the first error, and make sure to tell all later-called callbacks that they're too late and we have already failed. Except, if we got an error on an early callback but the 0th item comes back later, we need to do whatever we were going to do with that one piece of data.

    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

#18

Earlier 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.

The point is that a properly designed API wouldn't require any amount of scaffolding. You'd go:

    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

#19
post #13

I 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.

"Excel is not functional. It is declarative. You declare the relationships between the cells and Excel uses those to propagate changes."

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

#20

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…

> without introducing logic patterns foreign to many

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)

Post reply on HN