如何构建重复的Promise .then子句?

时间:2018-04-11 18:56:16

标签: javascript promise

如何重复.then子句作为循环?我需要等待.then子句承诺解决,然后如果没有满足退出条件,我需要再次重复相同的操作......直到满足退出条件。

我的用例是重复ajax GET调用多个“页面”数据,直到检索完所有数据。

我可以按如下方式简化和模拟问题。这是我希望.thenRepeat的.then条款:

  .then(function(retData) {
      namesList.push.apply(namesList, retData); // accum data into list
      queryParms.pageNo++;                      // set to next page
      return pretendAjaxFnc(queryParms);        // get next page
  })

这是一个可运行的例子:

function pretendAjaxFnc(obj) {
  return Promise.resolve().then(function() {
    if (obj.pageNo < 6) { // create a pretend "I'm done" point.
      // return a couple dummy records
      return [{  fld1: "data1",  fld2: "data2" }, 
              {  fld1: "data1",  fld2: "data2" }];
    } else {
      // this is the exit criteria
      // It will actually be 404 http status converted to a custom exception
      throw new Error("NO-MORE-DATA");
    }
  });
};

function clientAccumulator() {
  var namesList = [];
  var queryParms = {
    pageNo: 1
  };

  return pretendAjaxFnc(queryParms)
    .then(function(retData) {
      namesList.push.apply(namesList, retData); // append data to list
      queryParms.pageNo++; // set to get next page
      console.log("EIC.GTEST11 list.len: ", namesList.length);
      return pretendAjaxFnc(queryParms);
    })
    // repeat until some exit criteria - like an exception
    .then(function(retData) {
      namesList.push.apply(namesList, retData);
      queryParms.pageNo++;
      console.log("EIC.GTEST21 list.len: ", namesList.length);
      return pretendAjaxFnc(queryParms);
    })
    // repeat until some exit criteria - like an exception
    .then(function(retData) {
      namesList.push.apply(namesList, retData);
      queryParms.pageNo++;
      console.log("EIC.GTEST31 list.len: ", namesList.length);
      return pretendAjaxFnc(queryParms);
    })
    // repeat until some exit criteria - like an exception
    // ...
    .catch(function(ex) {
      if (ex.message === "NO-MORE-DATA") {
        return namesList;
      } else {
        throw ex;
      }
    });
};

clientAccumulator(); // Run the function

这是需要在当前iOS / Safari&amp; Firefox(尽管更多的变化)。我正在使用AngularJS,但我相信我已经删除了任何特异性。

有人有.thenRepeat他们可以指点我吗?

2 个答案:

答案 0 :(得分:1)

递归是你的朋友。

重新阅读之后,似乎这可能更符合您的需求:

repeatPromiseUntil({
    promiseMethod,
    doneConditionMethod,
    waitInterval = 100
}){
    return promiseMethod()
        .then((result) => {
            if (doneConditionMethod(result)) {
                return this.$q.resolve(result);
            }

            return this.$timeout(waitInterval)
                .then(() => this.repeatPromiseUntil({promiseMethod, doneConditionMethod, waitInterval}));
        });
}

这是我自己的代码中的一个例子(它是从这个答案的Typescript重写的,所以可能有一两个错字)

function retryPromise({
    maxRetries,
    promiseMethod,
    successConditionMethod,
    retryInterval,
    increaseInterval = true
}) {
    if (increaseInterval) {
        retryInterval = retryInterval + 1000;
    }

    return promiseMethod().then((result) => {
        if (successConditionMethod(result)) {
            return $q.resolve(result);
        } else if (maxRetries > 0) {
            return $timeout(retryInterval)
                .then(() => {
                    return retryPromise({
                        maxRetries: maxRetries - 1,
                        promiseMethod,
                        successConditionMethod,
                        retryInterval,
                        increaseInterval
                    });
                });
        } else {
            retryInterval = 0;
            return $q.reject();
        }
    });
}

编辑:我添加了一些我自己使用的角度细节(最初错过了有关角度的评论)

答案 1 :(得分:1)

只需将then链放入函数中,以便它是递归的:

  function recurse() {
    return pretendAjaxFnc(queryParams)
      .then(function(retData) {
         namesList.push(...retData);
         queryParms.pageNo++;
         return recurse();
    });
 }

工作示例:

function pretendAjaxFnc(obj) {
  return Promise.resolve().then(function() {
    if (obj.pageNo < 6) { // create a pretend "I'm done" point.
      // return a couple dummy records
      return [{  fld1: "data1", fld2: "data2" }, 
              {  fld1: "data1", fld2: "data2" }];
    } else {
      // this is the exit criteria
      // It will actually be 404 http status converted to a custom exception
      throw new Error("NO-MORE-DATA");
    }
  });
};

function clientAccumulator2() {
  var namesList = [];
  var queryParms = {
    pageNo: 1
  };
  console.log("TEST00 list.len: ", namesList.length);

  function recurse() {
    return pretendAjaxFnc(queryParms)
      .then(function(retData) {
        namesList.push(...retData);
        console.log("TEST01 list.len: ", namesList.length);
        queryParms.pageNo++;
        return recurse();
      });
  }

  return recurse()
    // repeat until some exit criteria - like an exception
    .catch(function(ex) {
      if (ex.message === "NO-MORE-DATA") {
        return namesList;
      } else {
        throw ex;
      }
    });
};
clientAccumulator2(); // Run the function