Promise.all()和捕获错误

Bra*_*roy 5 javascript fetch promise es6-promise

我写了一段代码片段,从Foursquare API中获取一些JSON.从这个JSON,我得到了场地的ID.然后,通过发出fetch()对每个ID 的请求,并将这些请求映射到数组中,可以使用这些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')
  }
Run Code Online (Sandbox Code Playgroud)

当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
Run Code Online (Sandbox Code Playgroud)

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

如何巧妙地捕获任何错误(包括那些通常不会被错误捕获的错误catch(),例如429错误)并getBackupData在发生错误时调用?

teh*_*wch 1

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

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

这个未捕获的承诺拒绝出现在处理以下解析的代码中fetch

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

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

[
  { /** some object for successful response */ },
  undefined,
  { /** some other object */ },
  ...
]
Run Code Online (Sandbox Code Playgroud)

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

return要修复您需要Promise.reject以及. 请注意,在某些情况下是隐式的,但我发现显式的更好,特别是当语句跨越多行时。由于您要返回该语句,因此您可以将其和卸载给调用者,从而减少一层嵌套,并减少一个重复处理程序。Promise.allreturnPromise.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')
}
Run Code Online (Sandbox Code Playgroud)