Я написал фрагмент кода, который получает JSON из Foursquare API. Из этого JSON я получаю идентификаторы заведений. Затем эти идентификаторы используются для получения дополнительных сведений об этих конкретных местах путем отправки запроса fetch() для каждого идентификатора и сопоставления этих запросов в массиве. Затем этот массив передается в 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.info('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.info(e); getBackupData()})
}).catch((e) => {console.info(e); getBackupData()})
function getBackupData() {
console.info('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
Я не понимаю, почему вводится then() после Promise.all (), потому что response никогда не является ok (в консоли нет зарегистрированного ok). Кроме того, я не понимаю, почему console.info() в блоках catch() не выполняются или почему они пусты. Я не вижу информации об обнаруженных ошибках в консоли, но функция getBackupData все равно вызывается. Наконец, неясно, почему последнее сообщение в консоли указывает, что ошибка непойманный, поскольку я ожидал, что reject() приведет к сбою Promise.all().
Как я могу тактично обнаруживать любые ошибки (включая те, которые обычно не обнаруживаются catch(), например, ошибки 429) и вызывать getBackupData при возникновении каких-либо ошибок?
@tehhowch хороший ответ, который вы получили ..... в качестве комментария.
@escapesequence выглядит как вопрос "из-за опечатки". Я не отвечаю на них. Теперь, когда я смотрю на код, есть еще несколько пропущенных операторов return, так что пора написать один.
@tehhowch достаточно честно



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Попробуйте вернуть отклоненное обещание.
return Promise.reject('Error when getting venue details')
В дополнение к вашему ответу, внутренний fetch() также должен быть возвращен функции map(). Ср. ответы теххоуча и Мартина Сарагосы о дополнительных исправлениях.
При работе с обещаниями вы должны возвращать внутренние обещания вместо работы с внутренними «тезисами».
Проверь это:
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.info('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.info(e); getBackupData()})
function getBackupData() {
console.info('backup')
}
Возвращая Promise.all в качестве значения, вы возвращаете обещание, чтобы вы могли связать дальнейшие обратные вызовы «затем». Последний улов должен захватить все отклоненные обещания.
Вам также не хватает возврата в предложении else
Надеюсь это поможет
В дополнение к вашему ответу, внутренний fetch() также должен быть возвращен функции map(). Ср. ответ теххоуча.
Ваши проблемы связаны: а именно, цепочка обещаний должна быть returned. Если вы не выполняете return Promise, вы отключаете любую обработку Promise#catch вызывающего абонента, и любые ошибки в вашем коде Promise / then приведут к необработанным ошибкам отклонения обещания, например тем, что вы получили:
Uncaught (in promise) Error when getting venue details
Это неперехваченное отклонение обещания появляется в вашем коде, который обрабатывает разрешение fetch:
if (response.ok) {
console.info('ok')
return response.json()
} else {
Promise.reject('Error when getting venue details') // <----
}
Поскольку этот код используется для создания вашего массива venuePromises, его значение return будет заполнять venuePromises. Если ответ был удовлетворительным, этот элемент массива будет иметь ответ JSON от return response.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. Обратите внимание, что есть некоторые случаи неявный 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.info('ok');
return response.json();
} else {
console.info(`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.info(e); getBackupData()});
function getBackupData() {
console.info('backup')
}
Ах, в этом есть смысл. Я ценю подробное объяснение. После применения ваших исправлений код действительно работает должным образом. Я думаю. На данный момент API возвращает ошибку 429 (что я не возражаю), и мой код возвращается к данным резервного копирования. Однако консоль Chrome по-прежнему показывает все неудачные запросы GET (ошибка 429). Означает ли это, что мне все еще не хватает некоторых ошибок, которые я не уловил, или это какое-то поведение браузера, которое невозможно обойти при обнаружении неудачных запросов GET?
Я не знаком с консолью Chrome, поэтому не могу вам помочь. Вероятно, вам также следует передать данные о месте проведения или данные резервной копии в this.parseFsqData (или что-то еще), то есть .then(venueData => venueData.map(entry => entry.response.venue)).catch(e => { console.info(e); return getBackupData()}).then(venues => { this.parseFsqData(venues); });
Я считаю, что решение довольно простое; В ответе вложенного метода выборки отсутствует оператор возврата. Вы должны избавиться от этой загадочной ошибки, как только она будет исправлена.
const venuePromises = venueIds.map(venueId => {
<missing return statement here> fetch(`https://api.foursquare.com/v2/venues/${venueId}?${otherParams}`)
.then(response => {
Вы не возвращаете этот
Promise.reject('Error when getting venue details'), поэтому индексvenuePromisesимеет значениеundefined, а не отклоненное обещание.