Looping with Promises

2019-06-23 00:11发布

问题:

I'm in a scenario where I have to get data from the server in parts in sequence, and I would like to do that with the help of Promises. This is what I've tried so far:

function getDataFromServer() {
  return new Promise(function(resolve, reject) {
    var result = [];

    (function fetchData(nextPageToken) {

      server.getData(nextPageToken).then(function(response) {
        result.push(response.data);
        if (response.nextPageToken) {
          fetchData(response.nextPageToken);
        } else {
          resolve(result);
        }
      });

    })(null);

  });
}

getDataFromServer().then(function(result) {
  console.log(result);
});

The first fetch is successful, but subsequent calls to server.getData() does not run. I presume that it has to do with that the first then() is not fulfilled. How should I mitigate this problem?

回答1:

Nimrand answers your question (missing catch), but here is your code without the promise constructor antipattern:

function getDataFromServer() {
  var result = [];

  function fetchData(nextPageToken) {
    return server.getData(nextPageToken).then(function(response) {
      result.push(response.data);
      if (response.nextPageToken) {
        return fetchData(response.nextPageToken);
      } else {
        return result;
      }
    });
  }
  return fetchData(null);
}

getDataFromServer().then(function(result) {
  console.log(result);
})
.catch(function(e) {
  console.error(e);
});

As you can see, recursion works great with promises.

var console = { log: function(msg) { div.innerHTML += "<p>"+ msg +"</p>"; }};

var responses = [
  { data: 1001, nextPageToken: 1 },
  { data: 1002, nextPageToken: 2 },
  { data: 1003, nextPageToken: 3 },
  { data: 1004, nextPageToken: 4 },
  { data: 1005, nextPageToken: 0 },
];

var server = {
  getData: function(token) {
    return new Promise(function(resolve) { resolve(responses[token]); });
  }
};

function getDataFromServer() {
  var result = [];

  function fetchData(nextPageToken) {
    return server.getData(nextPageToken).then(function(response) {
      result.push(response.data);
      if (response.nextPageToken) {
        return fetchData(response.nextPageToken);
      } else {
        return result;
      }
    });
  }
  return fetchData(0);
}

getDataFromServer().then(function(result) {
  console.log(result);
})
.catch(function(e) { console.log(e); });
<div id="div"></div>



回答2:

Because your then statement doesn't pass a function to handle error cases, requests to the server for data can fail silently, in which case the promise returned by getDataFromServer will never complete.

To fix this, pass a second function as an argument to then, as below:

function getDataFromServer() {
  return new Promise(function(resolve, reject) {
    var result = [];

    (function fetchData(nextPageToken) {

      server.getData(nextPageToken).then(function(response) {
        result.push(response.data);
        if (response.nextPageToken) {
          fetchData(response.nextPageToken);
        } else {
          resolve(result);
        }
      }).catch(function(error) {
        //Note: Calling console.log here just to make it easy to confirm this
        //was the problem.  You may wish to remove later.
        console.log("Error occurred while retrieving data from server: " + error);
        reject(error);
      });

    })(null);

  });
}