4

I have a REST service, offering a list of 'Json' objects, and each object may potentially have a link for another resource of its own class. Starting with a particular one, I need to fetch them all, performing a recursive http call. So I wrote:

var steps = [];
var recursiveLookup = function(processId) {
  return $.ajax({
    url: SERVER_URL + processId,
    success: function (activity) {
      // Activity has a set of json objects called steps
      var rtn = activity.path.map(step => {
        if (step.type != "Node") {
          steps.push(step);
        } else {
          return recursiveLookup(step.subProcessIntanceId);
        }
      }).filter(a => a != undefined);
      return rtn;
    }
  });
}

That would correctly load all objects into the global steps var. I need to be sure the method has finished, so I wrote:

var promises = recursiveLookup(processId);
Promise.all(promises).then(function () {
   console.log(steps);
});

But it's not working, as the 'recursiveLookup' is returning the promise of $.ajax, instead of the set of promises pretended to be returned with the success method.

Furthermore, is it possible to get the steps as a returned value from the 'recursiveLookup' method instead, of using it as a global variable?

El pocho la pantera
  • 505
  • 1
  • 5
  • 17
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Peter B Dec 29 '17 at 12:45
  • add async:false – Negi Rox Dec 29 '17 at 12:49
  • is it me or your function has 2 returns? I mean you first return rtn, then try to return the ajax itself. – Cr1xus Dec 29 '17 at 13:02
  • "Furthermore, is it possible to get the steps as a returned value from the 'recursiveLookup' method instead, of using it as a global variable?" You can use success: $.proxy(function(activity) { #code }, steps) – Cr1xus Dec 29 '17 at 13:10
  • Can't your server produce the entire recursive tree response in one request? – James Dec 29 '17 at 13:42
  • Could, but there's a scenario where I don't need the recursive calls, and performance it's not a problem (it's hosted on the same LAN) – El pocho la pantera Dec 29 '17 at 13:54

2 Answers2

1

Nested recursion is not within my confort zone but maybe this will work:

var recursiveLookup = function(processId,steps=[]) {
  return $.ajax({
    url: SERVER_URL + processId,
  })
  .then(
    function (activity) {
      // Activity has a set of json objects called steps
      steps = steps.concat(
        activity.path.filter(
          step => step.type !== "Node"
        )
      );
      return Promise.all(
        activity.path.filter(
          step => step.type === "Node"
        )
        .map(
          step=>
            recursiveLookup(step.subProcessIntanceId,steps)
        )
      ).then(
        result=>steps.concat(result)
      )
    }
  );
}

For tail call optimization to work the last thing the function does should be to call the recursive function but I think in promise chains it doesn't matter too much.

HMR
  • 37,593
  • 24
  • 91
  • 160
-2

You should not use the success parameter if you want to work with promises. Instead, you want to return a promise, and you want to use then to transform the results of a promise into something different, possibly even another promise.

function request(page) {    
…
// return the AJAX promise
return $.ajax({
    url: '/echo/json/',
    method: 'POST',
    dataType: 'json',
    data: {
        delay: 1,
        json: JSON.stringify(ret)
    }
 });
}

function requestOddsFrom(page, items) {
return request(page).then(function(data){
    if (data.currentPage > data.totalPage) {
        return items;
    } else {
        var filtered = data.items.filter(function(el){ return el%2 == 1; });
        return requestOddsFrom(data.currentPage + 1, items.concat(filtered));
    }
 });
}

function requestAll(){
return requestOddsFrom(1, []);
}

 requestAll().then(function(items) {
   console.dir(items);
});

for more info jQuery Recursive AJAX Call Promise

How do I return the response from an asynchronous call?

Negi Rox
  • 3,828
  • 1
  • 11
  • 18
  • That is absurd. `async:false` is a terrible practice and is deprecated. Using it will cause warnings in dev tools console not to ever use it – charlietfl Dec 29 '17 at 12:59
  • its depends on your need dude. not every language is perfect you need to modify it according to your need – Negi Rox Dec 29 '17 at 13:04
  • Well using deprecated approaches is not the way to do it. Especially in a recursive call! – charlietfl Dec 29 '17 at 13:06
  • Sorry Negi, that's realy the way it is. Maybe in Node you can get away with it but synchronous web requests in browser will hang everything up. You can check [here](https://stackoverflow.com/a/47678417/1641941) for information about why ES uses callback or promises for asynchronous operations (or operations that do not return a value immediately like network request) – HMR Dec 29 '17 at 13:10