Recursively calling asynchronous function that returns a promise

我的梦境 提交于 2020-01-23 17:49:26

问题


I'm trying to recursively call AWS's SNS listEndpointsByPlatformApplication. This returns the first 100 endpoints then a token in NextToken if there are more to return (details: AWS SNS listEndpointsByPlatformApplication).

Here's what I've tried:

var getEndpoints = function(platformARN, token) {

  return new models.sequelize.Promise(function(resolve, reject) {
    var params = {
      PlatformApplicationArn: platformARNDev
    };
    if (token != null) {
      params['NextToken'] = token;
    }
    sns.listEndpointsByPlatformApplication(params, function(err, data) {
      if (err) {
        return reject(err);
      }
      else {
        endpoints = endpoints.concat(data.Endpoints); //save to global var
        if ('NextToken' in data) {
          //call recursively
          return getEndpoints(platformARN, data.NextToken);
        }
        else {
          console.log('trying to break out!');
          return resolve(true);          
        }
      }
    });
  });
}

I'm calling it with:

getEndpoints(platformARNDev, null)
.then(function(ret) {
  console.log('HERE!');
}, function(err) {
  console.log(err);
});

Problem is: the first call happens, then the recursive call happens, and I get the message trying to break out! but the HERE! never gets called. I've got something wrong with how my promises are returning I think.

Grateful for pointers.


回答1:


The problem is that you try and resolve/reject partially completed query. Here is a complete working example with dummy service. I incapsulated the data grabbing into it's own recursive function and only do resolve/reject when i've completely fetched all the data or stumbled upon an error:

// This is the mock of the service. It yields data and token if
// it has more data to show. Otherwise data and null as a token.
var dummyData = [0, 1, 2, 3, 4];
function dummyAsyncCall(token, callback) {
  token = token || 0;
  setTimeout(function() {
    callback({
        dummyDataPart: dummyData[token],
        token: (typeof (dummyData[token]) == 'undefined') ? null : (token + 1)
    });
  });
}

// Here is how you would recursively call it with promises:

function getAllData() {
  //data accumulator is sitting within the function so it doesn't pollute the global namespace.
  var dataSoFar = [];

  function recursiveCall(token, resolve, reject) {
    dummyAsyncCall(token, function(data) {
      if (data.error) {
        reject(data.error);
      }
      if (!data.token) {
        //You don't need to return the resolve/reject result.
        resolve(dataSoFar);
      } else {
        dataSoFar = dataSoFar.concat(data.dummyDataPart);
        recursiveCall(data.token, resolve, reject);
      }
    });
  }

  return new Promise(function(resolve, reject) {
    // Note me passing resolve and reject into the recursive call.
    // I like it this way but you can just store them within the closure for
    // later use
    recursiveCall(null, resolve, reject);
  });
}

//Here is the call to the recursive service.
getAllData().then(function(data) {
  console.log(data);
});

Fiddle with me




回答2:


That's because you dont need to return resolve/reject, just call resolve/reject when the recursive call completes. A rough code would look like this

var getEndpoints = function(platformARN, token) {

  return new models.sequelize.Promise(function(resolve, reject) {
    var params = {
      PlatformApplicationArn: platformARNDev
    };
    if (token != null) {
      params['NextToken'] = token;
    }
    sns.listEndpointsByPlatformApplication(params, function(err, data) {
      if (err) {
        reject(err);
      }
      else {
        endpoints = endpoints.concat(data.Endpoints); //save to global var
        if ('NextToken' in data) {
          //call recursively
          getEndpoints(platformARN, data.NextToken).then(function () {
            resolve(true);
          }).catch(function (err) {
            reject(err);
          });
        }
        else {
          console.log('trying to break out!');
          resolve(true);          
        }
      }
    });
  });
}

(caution: this is just a rough code, may work or may not, but is to give a general idea)

I've added a code snippet below, to support this concept, and it works great, check it out.

i = 0;

$('#output').empty();

function pro() {
  return new Promise(function(resolve, reject) {
    if (i > 3) {
      resolve();
      return;
    }
    window.setTimeout(function() {
      console.log(i);
      $('#output').append(i).append('<br/>');
      i += 1;
      pro().then(function() {
        resolve()
      }).catch(function() {
        reject()
      });
    }, 2000);
  });
}

pro().then(function () { $('#output').append("now here"); })
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="output"></div>


来源:https://stackoverflow.com/questions/35814198/recursively-calling-asynchronous-function-that-returns-a-promise

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!