Search code examples
callbackcoffeescriptnestedunroll

How can I unroll callbacks in Coffeescript?


Normally in Javascript I can do something like this:

var step;
determineStep();

function determineStep() {
    step = 'A';
    asyncCallbackA(function(result)) {
        if (result.testForB) performB();
    });
}

function performB() {
    step = 'B';
    asyncCallbackB(function(result)) {
        if (result.testForC) performC();
    });
}

function performC() {
    step = 'C';
    ...
}

However Coffeescript does not allow named functions that get hoisted so I would have to define a function before calling it. This would result in them being out of order (very confusing). And if any of them have circular dependencies then it is not possible at all.

In Coffeescript I am forced to do:

step = null
determineStep =
    step = 'A'
    asyncCallbackA (result) ->
      if result.testForB
          step = 'B'
          asyncCallbackB (result) ->
              if result.testForC
                  step = 'C'
                  asyncCallbackC (result) ->
                      ...
determineStep()

If you have multiple steps this can quickly get out of hand.

Is it possible to implement the Javascript pattern in Cofffeescript? If not, what is the best way to handle this scenario?


Solution

  • I think you're a little confused. When you say:

    f = -> ...
    

    the var f is (of course) hoisted to the top of the scope but the f = function() { ... } definition is left where it is. This means that the only order that matters is that you need to define all your functions before you determineStep().

    For example, this works just fine:

    f = -> g()
    g = -> h()
    h = -> console.log('h')
    f()
    

    In your case:

    step = null
    
    determineStep = -> 
        step = 'A'
        asyncCallbackA (result) -> performB() if(result.testForB)
    
    performB = ->
        step = 'B'
        asyncCallbackB (result) -> performC() if(result.testForC)
    
    performC = ->
        step = 'C'
        ...
    
    determineStep()
    

    should be fine. determineStep can call performB before performB is defined (in source order) because:

    1. The var performB is hoisted.
    2. By the time determineStep executes, the performB = function() { ... } will have been done.

    Similarly for the other functions so you don't have to worry about interdependencies amongst your functions.