递归调用返回promise

时间:2016-03-05 12:35:49

标签: javascript amazon-web-services recursion promise

我试图以递归方式调用AWS的SNS listEndpointsByPlatformApplication。如果还有更多要返回的内容,则会返回前100个端点,然后返回NextToken中的令牌(详情:AWS SNS listEndpointsByPlatformApplication)。

以下是我尝试的内容:

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);          
        }
      }
    });
  });
}

我用以下方式调用它:

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

问题是:第一次调用发生,然后发生递归调用,我收到消息trying to break out!,但HERE!永远不会被调用。我认为我的承诺如何回归我有点不对劲。

感谢指针。

2 个答案:

答案 0 :(得分:4)

问题是您尝试解决/拒绝部分完成的查询。这是一个完整的虚拟服务工作示例。我把数据包含在它自己的递归函数中,只有当我完全获取所有数据或偶然发现错误时才解析/拒绝:

// 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

答案 1 :(得分:1)

那是因为您不需要返回解析/拒绝,只需在递归调用完成时调用resolve / reject。粗略的代码看起来像这样

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);          
        }
      }
    });
  });
}

警告:这只是一个粗略的代码,可能有用或可能没有,但是要提出一般性的想法)

我在下面添加了一个代码片段,以支持这个概念,并且效果很好,请查看。

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>