Settings

Theme

Faking Co-Routines, or Why Callback Hell Is Over (2014)

pandastrike.com

35 points by dyoder 10 years ago · 23 comments

Reader

erikpukinskis 10 years ago

I have yet to see an "improvement" on callbacks, whether it's promises or fibers or generators, where the benefit in readability is worth the havoc it wreaks on my ability to debug the program.

These days I write JavaScript using only functions, literals, variables, and the occasional prototype and it's amazing.

I think many programmers have a desire to believe they are working on complex problems that demand sophisticated tools. I remember learning Ruby and being excited whenever I found a reason to write a DSL. In retrospect it was unnecessary every time. In every case the code would've been clearer if I had just stuck with functions and kept refactoring until I had the right interfaces and data structures.

It helps to remember

    function a() {
      b(function() {
        //etc
      })
    }
is equivalent to

    function a() {
      b(c)
    }

    function c() {
      //etc
    }
which is not particularly more verbose. And as a side benefit, refactoring that way gives you an opportunity to make c() self-documenting.
  • cyphar 10 years ago

    > It helps to remember

    > function a() { b(function() { //etc }) } is equivalent to

    > function a() { b(c) } function c() { //etc } which is not particularly more verbose. And as a side benefit, refactoring that way gives you an opportunity to make c() self-documenting.

    It's not always equivalent, since you can have closures.

    • erikpukinskis 10 years ago

      That's true, and I use closures sometimes. But they are a performance and readability anti-pattern, and it's often better to either pass in or bind the data you actually need.

      In some sense closures are globals and globals are bad.

  • jestar_jokin 10 years ago

    Unfortunately, Node.js decided that every callback should accept an error as the first argument to every callback. If you're chaining a bunch of callbacks, it's tedious and error-prone to add boilerplate to check for an error and handle it consistently in every callback, and violating the DRY principle. It's not easy to simply propagate the error to a higher-level handler.

    • apeace 10 years ago

      I find the `async` library excels at this:

        var fs = require('fs');
        var async = require('async');
        async.waterfall([
          (cb) => fs.readFile('foo.txt', cb),
          (data, cb) => my_function(data, cb),
          (result, cb) => myDb.lookup(result, cb),
        ], (err, finalResult) {
          if (err) {
            console.error(err);
          } else {
            console.log(finalResult);
          }
        });
      
      There are all sorts of helpful async primitives in there. I don't write Javascript without it!

      Here is a link to the documentation: https://github.com/caolan/async

    • erikpukinskis 10 years ago

      I would have to look at your code, but generally if you have a long callback chain and you're trying to propagate errors up it you are doing something wrong. Are you really working on 10 nested activities simultaneously, or are you just nesting things because it's convenient and you don't want to think about how to do things in stages?

      If you're trying to convince me that "callbacks become painful in situation X" you really don't need to. I know that. What I'm saying is that 9 times out of 10, the answer to the question "do we really want to be in situation X in the first place?" is "no". But instead of getting out of a bad situation by refactoring, people just write crazier and crazier control structures (i.e. promises) to make those bad situations workable.

    • jjn2009 10 years ago

      Its a problem yes, but the nature of javascript makes this a difficult problem to address. Node tried addressing this with the 'domain' module to help with propagating errors upward more generically:

      https://nodejs.org/api/domain.html

      But it was deprecated: https://github.com/nodejs/node/issues/66

      promises are a very nice way of drying out error handling and cleaning up callbacks for now.

    • fao_ 10 years ago

      If you're repeating yourself, then you could possibly abuse higher class functions to automatically generate error handling?

  • mateuszf 10 years ago

    > I have yet to see an "improvement" on callbacks, whether it's promises or fibers or generators, where the benefit in readability is worth the havoc it wreaks on my ability to debug the program.

    Chrome has support for debugging async code - for example you can step from one code block to the code in callback as if it would be executed sequentially. So the 'debuggability' is a problem which could be solved on the side of the tools. Of course the case where there would be one single standard for handling async would vastly simplify situation for the tools developers.

outside1234 10 years ago

Please: Do not use CoffeeScript in your examples.

It is not common to have "readability" in CoffeeScript and it restricts a significant number of readers from being able to understand the code sample.

  • mchahn 10 years ago

    > restricts a significant number of readers from being able to understand

    Can you seriously say you can't read it? It seemed easy to me.

Azkar 10 years ago

I don't understand why he talks about a javascript problem being solved, then goes on to show us examples in coffee script.

pspeter3 10 years ago

I still don't get why async/await is inherently better than using Promises/Streams. Is it purely a syntax difference or is there a semantic difference as well?

  • jestar_jokin 10 years ago

    I believe async/await uses promises behind the scenes. It just means invoking code looks nicer, i.e writing "var res1 = await prom1(a); await prom2(a, res1)" is marginally cleaner than "return prom1(a).then((res1) => prom2(a, res1));", in the sense that you're just writing statements rather than chaining method invocations.

scriby 10 years ago

https://github.com/scriby/asyncblock-generators

That's a control flow solution I wrote on top of generators to make it a little easier to manage parallel tasks, timeouts, error handling, and so on.

I originally made asyncblock, which was based on fibers a few years ago. This module uses the same underpinnings as asyncblock, just based in generators instead of fibers.

amelius 10 years ago

The problem with javascript coroutines is that you can only yield from within the generator itself, not from a called function.

This makes it impossible, for instance, to write a nice I/O library that is to be called from within a generator (the library is supposed to yield on a blocking situation).

teddyh 10 years ago

What’s with the HUGE FONT SIZE?

raspasov 10 years ago

Use ClojureScript + core.async and get some work done.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection