Live data from Hacker News

Escape from Callback Hell

ianbishop.github.com

31–40 of 86 posts

Re: Escape from Callback Hell

#31

Earlier quoted context omitted.

doing/not doing IO may be an implementation detail for a function. For example, say you have a function isPrime(x) that you implement using some primality test, with no IO. At some point in the future, you may notice that computing the primality test takes a long time, so instead you decide to submit the number to the server which will then return the answer. The function itself remains the same, it takes in a number…

The function isn't the same, it now how side effects.

Not conceptually. From the perspective of the rest of the program, isPrime(x) has no side effect regardless of which method you use to implement it. If you are working in a purely functional language, then it makes sense to have introducing IO be a major change, as it has huge potential to make a function impure, and assuming IO is a pure function is roughly the equivalent of unsafePerformIO. However, in this case, we are not even working in a pure functional language. In concept, there is no reason that implementing a function with IO should require writing code outside of that function differently than implementing it with CPU.

Re: Escape from Callback Hell

#32
post #2

I honestly find "callback hell" a lot easier to follow and understand than the vast majority of fixes everyone is coming up with. They're just continuations, seriously, what's everyone's problem? You define a function, it gets access to the current scope, it defines the rest of the program flow. If you feel like your code is nesting too deep, you define the function elsewhere and just reference it by name. Then you d…

And you do this when you have 10 different steps and each one of those steps should also have an error case. And you do this for 100s of resources and you have callback hell.

If you start seeing do1() do2().. or cb1(), cb2() and so on function that is the "hell" everyone is talking about. Of course you should name your functions better -- but that's not the point. Logically you might not need an extra function but you are forced to add it because of the way your framework forced you to handle IO.

Re: Escape from Callback Hell

#33

Earlier quoted context omitted.

That isnt the point, its that you have some nice code that does |var x = someBigFunction()| and at some point in the programs life, something inside someBigFunction turns async, you have a whole restructuring to do.

This is just not something that happens in real life. You know what functions are going to be async up-front, and likely make them async even if you don't know, just in case. Since all IO in js is async by default, having a code that does a simple calculation and introducing IO into it is actually a very big change that warrants the refactoring you'll be required to do.

> This is just not something that happens in real life.

You never change code in real life? You don't always know what functions are going to be async up-front. They might become async later, 4 levels down, when someone decides to do some IO. What happens then? Exactly. You have to ripple that code all the way up to the top of the API.

Re: Escape from Callback Hell

#34
I recently also tried my hand at promises using the node libs Q and when.

There's a gotcha with the progress handler. If you try to call the progress handler before the progress handler actually gets a chance to attach itself outside the function, it'll never actually fire. Some of the bugs with using promises are rather subtle.

Re: Escape from Callback Hell

#35
post #4

It seems like this problem would be elegantly solved by starting a thread, green thread or coroutine (depending on language) for each task and calling the API functions synchronously from within that. I'm not sure what support JS has for these things.

JS has no support for these things, but you're right, when using languages (like Go) that do support this concept, it is very easy to write elegant code which can block locally without actually blocking the entire application, which allows you to write code that is far cleaner and easier to reason about.

Re: Escape from Callback Hell

#37

I dont find that promises really help callback hell that much, they are useful but in the case of doing a series of sequential async functions the result is pretty similiar (the promises version is usually longer) I got to write some firefox only code recently and added a delay to a function by just adding ... some code setTimeout(continueFun, 5000) yield; ... more code it felt like magic, I dont like generators and…

I made a library called async.js (https://github.com/eligrey/async.js) that makes it even easier to abstract away all callbacks with yields, so all you'd have to do is yield to.sleep(5); continueFun(); for your example.

More complex things are also easy to do as well, such as implementing a node.next(event) method, so you can simply do var click = yield document.next("click"); instead of the whole event listener shebang. The feedback example (https://github.com/eligrey/async.js#asking-the-user-for-thei...) shows how much of a difference using yield can be versus callbacks or even promises.

Re: Escape from Callback Hell

#38

I dont find that promises really help callback hell that much, they are useful but in the case of doing a series of sequential async functions the result is pretty similiar (the promises version is usually longer) I got to write some firefox only code recently and added a delay to a function by just adding ... some code setTimeout(continueFun, 5000) yield; ... more code it felt like magic, I dont like generators and…

Promises are not an alternative to callbacks - they are a wrapper around them that make working with them much nicer (though still a little painful as you imply). They add composability which I think has wider implications than many realize at first.

Consider that with things like jQuery deferreds I can start a request in one function and pass it around adding handlers in others...something very hard to do with the old CB model. Maybe later on you have some optional action that you only want to occur if a previous action had completed. This sort of thing is easy with deferreds. It makes bundling together disparate actions easier as well as handling series of things as you mention.

Having an abstraction layer in there also has a lot of potential that is (somewhat) untapped. There is a lot of possibility for library authors to add excellent error handling or debugging capabilities in there - things that are currently a bit painful with POC (plain 'ol callbacks).

I agree generators have strong possibilities in this area- I would note thought that they are not an alternative to promises - the two are orthogonal, and in fact they work quite well together. Just take a look at what some other Mozilla folks are doing with http://taskjs.org/ for example.

Re: Escape from Callback Hell

#39
> "This is better, it definitely makes it clearer what exactly is going on"

    function createItem(item, successCallback, errorCallback) {
      var req = $.post("/api/item", item);
      req.success(successCallback);
      req.error(errorCallback);
    }
How is this more clear than the default jQuery $.post?

Re: Escape from Callback Hell

#40

Earlier quoted context omitted.

A function that doesn't do IO and a function that does IO sound like totally different functions to me. Why aren't you writing a new function?

doing/not doing IO may be an implementation detail for a function. For example, say you have a function isPrime(x) that you implement using some primality test, with no IO. At some point in the future, you may notice that computing the primality test takes a long time, so instead you decide to submit the number to the server which will then return the answer. The function itself remains the same, it takes in a number…

generally you are right, it would be far easier to hide these kind of choices if the language allowed it.

However currently javascript has a purely event driven model and this means that you have trouble fitting in even a purely CPU bound computation, as it would block the responsiveness of your UI (or other concurrent requests if your are doing server side JS). That's why there was a need for creating something like WebWorkers.

Post reply on HN