deshacerse del anidamiento requerido para las devoluciones de llamada de javascript

I've got some javascript functions that take perform asynchronous requests over the network and take a callback to "return" data via. Here is an example of what this looks like when I use it:

RemoteQuery(server,query,function(ret) {
  // do something with ret
})

The problem is, if I have several queries in a row the code become quite nested and difficult to manage. Since I am relying on scoped variables, I can't rip out each of these functions into a separate top-level function either. Here is a toy example:

RemoteQuery(server,query1,function(ret) {
      var x = ret[5]
      RemoteQuery(server,query2,function(ret) {
          var y = ret[3]
          if (x + y > 10) {
              RemoteQuery(server,query2,function(ret) {
                  // do more stuff
              })
          }      
      })
})

Obviously if I have more than 2 or 3 queries it starts getting ugly, and I might have a lot more than that!

Ideally, I would like to represent the above without all that nesting, e.g.

ret = RemoteQuery(server,query1)
var x = ret[5]
ret = RemoteQuery(server,query2)
var y = ret[3]
if (x + y > 10) {
    ret =RemoteQuery(server,query2)
    // do more stuff
}

but the only thing I have thought of that might work would be to parse the javascript, recognize functions which have callbacks, re-write them in the correct form, and eval, but this seems awfully complicated and would make debugging very difficult.

Is there a better mechanism to do this?

preguntado el 30 de junio de 12 a las 20:06

how about an Array of functions? -

jQuery has this Deferred technique that can help you, but I don't know if it's ok for you to use it. -

3 Respuestas

The 'proper' way to do it is to write function declarations for them and avoid relying on the scoped variables (so pass them to each function). Something like:

var myFunc1 = function(x, ret1) { 
  RemoteQuery(server, query, function(ret2) { 
    var y = ret[3];
    myFunc2(x, y, ret2);
  });
};

var myFunc2 = function(x, y, ret) { /* do more stuff */ };

RemoteQuery(server, query, function(ret1) {
  var x = ret[5];
  myFunc1(x, ret1);
});

Since you have to call those RemoteQuery(ies) asynchronously, there's no way really to do the solution you suggest in your question. Use of the Deferred object may do the trick but I think you'd still have to restructure your functions (since you're aiming to avoid the nestedness).

Respondido el 30 de junio de 12 a las 20:06

For anyone referencing this in future, I picked up this model when I learned NodeJS as this is how most of the demo/example apps are built (Node usually requires a lot of nesting). - bob davies

If you're willing to use a framework both JQuery al igual que Dojo (I'm sure others have it too!) contain a deferred object which would be a nice alternative.

Respondido el 30 de junio de 12 a las 20:06

How about an array of chained functions?

function R1 (server, query, callback)
{
    RemoteQuery(server, query, function(ret) {
        // process ret
        if (x + y > 10)
          callback();
    }
}

function R2 (server, query, callback)
{    
    RemoteQuery(server, query, function(ret) {
        // process ret
        callback();
    }
}

function R3 (server, query, callback)
{
    RemoteQuery(server, query, function(ret) {
        // process ret
        callback();
    }
}

var functions = [R1, R2, R3]

function callAll(a, server, query)
{
    var callNext = function(callback, i)
    {
        if (i < a.length)
            a[i](server, query, function() { callback(callback, i + 1); });
    }
    callNext(callNext, 0);
}

callAll(functions, 'server', 'query');

Respondido el 30 de junio de 12 a las 20:06

this way you do not need all those jquerys and dojos etc. ;] - Kuba Wyrostek

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.