Promise.all()和捕获错误

时间:2018-08-14 16:53:45

标签: javascript promise fetch es6-promise

我编写了一段代码,该代码从Foursquare API中获取了一些JSON。从这个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登录)。另外,我不明白为什么console.log()块中的catch()不被执行,或者为什么它们为空。我在控制台中看不到任何捕获的错误信息,但是仍然调用getBackupData函数。最后,目前还不清楚为什么控制台中的最后一条消息指出错误是未被捕获,正如我期望reject()会使Promise.all()失败一样。

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

4 个答案:

答案 0 :(得分:1)

尝试返回被拒绝的承诺。

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

答案 1 :(得分:1)

使用诺言时,您应该返回使用内部“ 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子句中的返回结果

希望这会有所帮助

答案 2 :(得分:1)

您的问题与之有关:即,必须WinForms建立Promise链。如果您没有public partial class Form1 : Form { private Button btnSubmit; private Label lblGuess; private TextBox txtGuess; private Label lblWord; private Label lblGuessedLetters; private ListBox lstGuessedLetters; private readonly BindingList<char> guesses = new BindingList<char>(); private const string WordToGuess = "StackOverflow"; private const int controlHeight = 20; private const int controlWidth = 200; private const int controlPadding = 10; public Form1() { InitializeComponent(); lblWord = new Label { Left = controlPadding, Top = controlPadding, Height = controlHeight, Width = controlWidth, Text = new string('_', WordToGuess.Length) }; Controls.Add(lblWord); lblGuessedLetters = new Label { Left = controlPadding, Top = lblWord.Bottom + controlPadding, Height = controlHeight, Width = controlWidth, Text = "Guessed Letters:" }; Controls.Add(lblGuessedLetters); lstGuessedLetters = new ListBox { Left = controlPadding, Top = lblGuessedLetters.Bottom + controlPadding, Height = controlHeight * 5, Width = controlWidth, Enabled = false, DataSource = guesses, SelectionMode = SelectionMode.None }; Controls.Add(lstGuessedLetters); lblGuess = new Label { Left = controlPadding, Top = lstGuessedLetters.Bottom + controlPadding, Height = controlHeight, Width = controlWidth, Text = "Enter your guess below" }; Controls.Add(lblGuess); txtGuess = new TextBox { Left = controlPadding, Top = lblGuess.Bottom + controlPadding, Height = controlHeight, Width = controlWidth }; Controls.Add(txtGuess); btnSubmit = new Button { Left = controlPadding, Top = txtGuess.Bottom + controlPadding, Height = controlHeight, Width = controlWidth, Text = "Submit Guess" }; btnSubmit.Click += BtnSubmit_Click; AcceptButton = btnSubmit; // Make this button default - just press Enter Controls.Add(btnSubmit); } private void BtnSubmit_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(txtGuess.Text)) { MessageBox.Show("enter a guess in the textbox"); } else { // Grab the first letter in case they typed more than one var letter = txtGuess.Text[0]; if (guesses.Contains(letter)) { MessageBox.Show("you already guessed that letter!"); } else { guesses.Add(letter); lblWord.Text = string.Join(" ", WordToGuess.Select(chr => guesses.Contains(chr) ? chr : '_')); } } txtGuess.Focus(); txtGuess.Text = ""; } } Promise,则断开任何呼叫者的return处理,并且您的Promise / return代码中的任何错误都将导致未处理的承诺拒绝错误,例如您已获得:

  

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

此未解决的承诺拒绝出现在处理Promise#catch分辨率的代码中:

then

由于此代码用于构造您的fetch数组,因此其if (response.ok) { console.log('ok') return response.json() } else { Promise.reject('Error when getting venue details') // <---- } 的值将填充venuePromises。如果响应正常,则该数组元素将具有来自return的响应JSON。如果响应失败,则不会执行任何venuePromises语句,因此数组元素的值为return response.json()。因此,return看起来像这样:

undefined

因此,当venuePromises的成功处理程序访问此数组时,由于您希望[ { /** some object for successful response */ }, undefined, { /** some other object */ }, ... ] 的所有元素均有效,因此会收到TypeError。 Promise.all的{​​{1}}处理程序捕获了此TypeError(这就是为什么会记录该错误,并且您在日志中收到“备份”文本的原因)。

要修复,您需要venuePromises Promise.all以及 .catch。请注意,在某些情况下,implicit return更为明确,特别是在语句跨越多行的情况下。由于您要返回return语句,因此可以将其Promise.rejectPromise.all卸载到调用方,从而减少了嵌套级别,并减少了重复的Promise.all处理程序。

.then

答案 3 :(得分:1)

我认为解决方案非常简单;嵌套的fetch方法的响应缺少return语句。一旦到位,您就应该摆脱这个神秘的错误。

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