我编写了一段代码,该代码从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
?
答案 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.reject
和Promise.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 => {