【问题标题】:Looping with Promises使用 Promise 循环
【发布时间】:2015-07-22 22:17:41
【问题描述】:

我在一个场景中,我必须按顺序从服务器获取数据,我想在 Promises 的帮助下做到这一点。这是我迄今为止尝试过的:

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

第一次获取成功,但对server.getData() 的后续调用没有运行。我认为这与第一个 then() 未完成有关。我应该如何缓解这个问题?

【问题讨论】:

  • 所以,console.log(result);不被调用/不打印任何东西?
  • @Joel 没错。如果我要在getData().then() 中登录,我只会得到第一部分的响应,而不是后续的。
  • 代码看起来应该可以工作。尝试在对“then”的调用中添加第二个函数来处理错误情况(至少现在记录错误)。我怀疑对 server.getData 的第二次调用静默失败。无论如何,您可能需要处理这些类型的错误。
  • 第一个“response.nextPageToken”是什么?可能是 0 吗?在什么情况下,它会立即停止? (但这意味着承诺已经解决,你说的不是);我在这里尝试了类似的代码gist.github.com/jotak/1db63cb48f90e86dca74 并且效果很好,所以问题可能出在服务器端
  • @Nimrand 确实失败了 :) 还不习惯捕获所有 Promise!非常感谢。

标签: javascript asynchronous promise


【解决方案1】:

Nimrand 回答了您的问题(缺少catch),但这里是您的代码没有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);
});

如您所见,递归与 Promise 配合得很好。

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); });
&lt;div id="div"&gt;&lt;/div&gt;

【讨论】:

  • 那么,当函数f 传递给then 恰好返回一个promise a 时,then 返回的promise 是否也等待a 完成?通常,then 返回的承诺会在f 完成后立即完成,这意味着这段代码不一定能工作。
  • 这太棒了。谢谢吉布!越来越喜欢 Promises。 :)
  • 对,当一个 promise resolved 到另一个 pending 的 promise 时,第一个 promise 保持 pending 并且不会在第二个承诺解决之前实现。在这种情况下,使用states and fates 中的术语,第一个承诺既resolvedpending,但不是fulfilled
  • 谢谢。它在强类型语言(如 Scala)中的工作方式略有不同,所以起初这对我来说并不直观。但是,这是有道理的。
【解决方案2】:

由于您的 then 语句没有传递处理错误情况的函数,因此对服务器的数据请求可能会静默失败,在这种情况下 getDataFromServer 返回的承诺将永远不会完成。

要解决此问题,请将第二个函数作为参数传递给 then,如下所示:

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

  });
}

【讨论】:

  • 最好完全避免promise constructor antipattern
  • 我考虑重构代码以避免这种延迟的承诺构造,但因为它的递归和 server.getData 将被调用的次数是不确定的,我看不出有什么办法。但是,我对 JavaScript 中的 Promise 不熟悉(尽管我在其他语言中使用过它们),所以如果您知道解决此问题的方法,请告诉我。
  • reject(error) 是对的,但我会让错误在记录之前冒泡。即getDataFromServer().then(result =&gt; console.log(result)).catch(e =&gt; console.error(e)); - 总是返回 用 catch 终止链,这是我使用的规则。
  • 好点。在这里使用 catch 稍微安全一些。不同之处在于,即使在传递给then 的函数中发生异常,您也会捕获异常,尽管那里没有发生任何应该能够因异常而失败的事情。
  • @Nimrand:递归 FTW!查看 jib 的回答
猜你喜欢
  • 1970-01-01
  • 2020-07-27
  • 2019-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-08
  • 2016-12-13
相关资源
最近更新 更多