【问题标题】:Promise.all() and catching errorsPromise.all() 和捕获错误
【发布时间】:2019-01-21 14:26:23
【问题描述】:

我编写了一段从 Foursquare API 获取 JSON 的 sn-p 代码。从这个 JSON 中,我得到了场地的 ID。然后,通过为每个 ID 发出 fetch() 请求并将这些请求映射到数组中,这些 ID 用于从这些特定场所获取更多详细信息。然后将该数组传递给Promise.all()。当 API 可用时,一切正常,但我无法理解的是错误捕获。

fetch(`https://api.foursquare.com/v2/venues/search?${params}`)
  .then(response => response.json())
  .then(data => {
      const venueIds = data.response.venues.map(venue => venue.id)

      const venuePromises = venueIds.map(venueId => {
        fetch(`https://api.foursquare.com/v2/venues/${venueId}?${otherParams}`)
          .then(response => {
            // Must check for response.ok, because 
            // catch() does not catch 429
            if (response.ok) {
              console.log('ok')
              return response.json()
            } else {
              Promise.reject('Error when getting venue details')
            }
          })
      })

      Promise.all(venuePromises).then(data => {
        const venues = data.map(entry => entry.response.venue)  // Error for this line
        this.parseFsqData(venues)
      }).catch((e) => {console.log(e); getBackupData()})
  }).catch((e) => {console.log(e); getBackupData()})

  function getBackupData() {
    console.log('backup')
  }

当 API 不可用时,我会收到以下控制台错误(以及更多相同的错误):

TypeError: Cannot read property 'response' of undefined
    at MapsApp.js:97
    at Array.map (<anonymous>)
    at MapsApp.js:97

backup

api.foursquare.com/v2/venues/4b7efa2ef964a520c90d30e3?client_id=ANDGBLDVCRISN1JNRWNLLTDNGTBNB2I4SZT4ZQYKPTY3PDNP&client_secret=QNVYZRG0JYJR3G45SP3RTOTQK0SLQSNTDCYXOBWUUYCGKPJX&v=20180323:1 Failed to load resource: the server responded with a status of 429 ()

Uncaught (in promise) Error when getting venue details

我不明白为什么在输入 Promise.all() 之后then(),因为response 永远不是ok(控制台中没有ok 登录)。另外,我不明白为什么catch() 块中的console.log() 没有被执行,或者为什么它们是空的。我在控制台中没有看到任何捕获的错误信息,但仍然调用了 getBackupData 函数。最后,不清楚为什么控制台中的最后一条消息表明错误是未捕获,因为我预计reject() 会使Promise.all() 失败。

如何巧妙地捕捉到任何错误(包括那些通常不会被catch() 捕捉到的错误,例如429 错误)并在出现任何错误时调用getBackupData

【问题讨论】:

  • 你不会返回那个Promise.reject('Error when getting venue details'),所以venuePromises的索引值是undefined,而不是一个被拒绝的Promise。
  • @tehhowch 你得到了很好的答案.....作为评论。
  • @escapesequence 感觉像是一个“由错字引起”的问题。我不回答这些。现在我查看了更多代码,但还有更多遗漏的 return 语句,所以是时候写一个了
  • @tehhowch 够公平

标签: javascript promise fetch es6-promise


【解决方案1】:

您的问题是相关的:即,Promise 链必须是returned。如果你不return Promise,你断开任何调用者的Promise#catch 处理,你的 Promise / then 代码中的任何错误都会导致未处理的 Promise 拒绝错误,例如你得到的:

获取场地详细信息时未捕获(承诺)错误

这个未捕获的 Promise 拒绝出现在处理 fetch 解析的代码中:

if (response.ok) {
  console.log('ok')
  return response.json()
} else {
  Promise.reject('Error when getting venue details')  // <----
}

由于此代码用于构造您的venuePromises 数组,因此其return 值将填充venuePromises。如果响应正常,则该数组元素将具有来自return response.json() 的响应 JSON。如果响应失败,则没有执行return 语句,因此数组元素的值为undefined。因此,venuePromises 看起来像这样:

[
  { /** some object for successful response */ },
  undefined,
  { /** some other object */ },
  ...
]

因此,当您的 Promise.all 的成功处理程序访问此数组时,您会收到 TypeError,因为您希望 venuePromises 的所有元素都有效。此 TypeError 被 Promise.all.catch 处理程序捕获(这就是它被记录的原因,并且您会在日志中收到“备份”文本)。

要修复,您需要return Promise.reject还有 Promise.all。请注意,implicit return 有一些情况,但我发现明确表示更好,特别是如果语句跨越多行。由于您要返回 Promise.all 语句,因此您可以将其 .then.catch 卸载到调用者,从而减少嵌套级别和重复的 .catch 处理程序。

fetch(`https://api.foursquare.com/v2/venues/search?${params}`)
    .then(response => response.json())
    .then(jsonData => {
        const venueIds = jsonData.response.venues.map(venue => venue.id);
        const venuePromises = venueIds.map(venueId => {
            let link = `https://api.foursquare.com/v2/venues/${venueId}?${otherParams}`;
            return fetch(link).then(response => {
                // Must check for response.ok, because catch() does not catch 429
                if (response.ok) {
                    console.log('ok');
                    return response.json();
                } else {
                    console.log(`FAILED: ${link}`);
                    // Return a Promise
                    return Promise.reject(`Error when getting venue details for '${venueId}'`);
                }
            });
        });

        return Promise.all(venuePromises);
    })
    .then(venueData => {
        const venues = venueData.map(entry => entry.response.venue);
        this.parseFsqData(venues);
    })
    .catch(e => {console.log(e); getBackupData()});

function getBackupData() {
    console.log('backup')
}

【讨论】:

  • 啊,这很有道理。我很欣赏详尽的解释。应用修复后,代码确实按预期运行。 我认为。 API 现在返回 429 错误(我不介意),我的代码依赖于备份数据。但是,Chrome 的控制台仍然显示所有失败的 GET 请求(错误 429)。这是否意味着我仍然遗漏了一些我没有发现的错误,或者这是您在遇到失败的 GET 请求时无法解决的一些浏览器行为?
  • 我对 Chrome 的控制台不熟悉,所以无法为您提供帮助。您可能还应该将场地数据或备份数据传递给this.parseFsqData(或其他东西),即.then(venueData =&gt; venueData.map(entry =&gt; entry.response.venue)).catch(e =&gt; { console.log(e); return getBackupData()}).then(venues =&gt; { this.parseFsqData(venues); });
【解决方案2】:

尝试返回被拒绝的承诺。

return Promise.reject('Error when getting venue details')

【讨论】:

  • 除了你的答案,内部的fetch()也应该返回给map()函数。参照。 tehhowch 和 Martín Zaragoza 对其他修复的回答。
【解决方案3】:

使用 Promise 时,您应该返回内部 Promise,而不是使用内部“thens”。

检查一下:

fetch(`https://api.foursquare.com/v2/venues/search?${params}`)
  .then(response => response.json())
  .then(data => {
      const venueIds = data.response.venues.map(venue => venue.id);

      const venuePromises = venueIds.map(venueId => {
        fetch(`https://api.foursquare.com/v2/venues/${venueId}?${otherParams}`)
          .then(response => {
            // Must check for response.ok, because 
            // catch() does not catch 429
            if (response.ok) {
              console.log('ok')
              return response.json()
            } else {
              return Promise.reject('Error when getting venue details')
            }
          })
      });

      return Promise.all(venuePromises)
  })
  .then(venueValues => {
    const venues = venueValues.map(entry => entry.response.venue);  // Error for this line
    this.parseFsqData(venues);
  })
  .catch((e) => {console.log(e); getBackupData()})


function getBackupData() {
    console.log('backup')
}

将 Promise.all 作为值返回时,您将返回一个 Promise,以便您可以链接更多的“then”回调。最后一次捕获应捕获所有拒绝的承诺。

你也错过了 else 子句中的返回

希望对你有帮助

【讨论】:

  • 除了你的答案,内部的fetch()也应该返回给map()函数。参照。 tehhowch 的回答。
【解决方案4】:

我相信解决方案相当简单;嵌套 fetch 方法的响应缺少 return 语句。一旦它到位,你应该摆脱那个神秘的错误。

const venuePromises = venueIds.map(venueId => {
    <missing return statement here> fetch(`https://api.foursquare.com/v2/venues/${venueId}?${otherParams}`)
      .then(response => {

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 2020-09-19
    • 2020-03-27
    • 2019-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多