Как вы уже узнали, 429 означает, что вы отправляете слишком много запросов:
Пользователь отправил слишком много запросов в заданный промежуток времени («ограничение скорости»).
Представления ответа ДОЛЖНЫ включать подробности, поясняющие условие, и МОГУТ включать заголовок Retry-After, указывающий, как долго ждать перед выполнением нового запроса.
Например:
HTTP/1.1 429 Too Many Requests
Content-Type: text/html
Retry-After: 3600
<html>
<head>
<title>Too Many Requests</title>
</head>
<body>
<h1>Too Many Requests</h1>
<p>I only allow 50 requests per hour to this Web site per
logged in user. Try again soon.</p>
</body>
</html>
Обратите внимание, что эта спецификация не определяет, как сервер-источник идентифицирует пользователя и как он считает запросы. Например, исходный сервер, который ограничивает скорости запросов, может делать это на основе количества запросов по каждому ресурсу, по всему серверу или даже среди набора серверов. Аналогичным образом, он может идентифицировать пользователя по его учетным данным для проверки подлинности или по состоянию готовности ie.
Ответы с кодом состояния 429 НЕ ДОЛЖНЫ храниться в кэше.
Кому Чтобы решить эту проблему, вы должны уменьшить количество запросов, сделанных за определенное время. Вам следует перебирать свои коды с задержкой, разнося запрос на несколько секунд. Если не указано в ответе 429, вы должны использовать метод проб и ошибок, чтобы найти задержку, которая работает. В приведенном ниже примере я разнес их на 2 секунды (2000 миллисекунд).
Это можно сделать с помощью setTimeout()
для выполнения некоторого фрагмента кода позже, в сочетании с Обещанием создайте функцию sleep
. Затем, используя async function
сна каждую итерацию в течение определенного c времени.
Примером может быть:
const fetch = createFetchMock({
"/some-code": ["abcde", "fghij", "klmno", "pqrst"],
"/abcde": 12345,
"/fghij": 67891,
"/klmno": 23456,
"/pqrst": 78912,
});
async function delayedForEach(array, delay, fn) {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
for (let i = 0; i < array.length; ++i) {
await sleep(delay);
fn(array[i], i, array);
}
}
async function delayedMap(array, delay, fn) {
const result = [];
await delayedForEach(array, delay, (...args) => result.push(fn(...args)));
return result;
}
fetch("http://my-url/some-code")
.then(respons => respons.json())
.then(codes => {
return delayedMap(codes, 2000, code => {
const url = `http://my-url/${code}`;
console.log("fetching url", url);
return Promise.all([code, fetch(url).then(response => response.json())]);
});
})
.then(codeNumberPromises => Promise.all(codeNumberPromises))
.then(codeNumbers => {
const codesObj = Object.fromEntries(codeNumbers);
console.log("codesObj:", codesObj);
})
.catch(error => console.error(error));
// fetch mocker factory
function createFetchMock(dataByPath = {}) {
const empty = new Blob([], {type: "text/plain"});
const status = {
ok: {status: 200, statusText: "OK"},
notFound: {status: 404, statusText: "Not Found"}
};
const blobByPath = Object.create(null);
for (const path in dataByPath) {
const json = JSON.stringify(dataByPath[path]);
blobByPath[path] = new Blob([json], {type: "application/json"});
}
return function (url) {
const path = new URL(url).pathname;
const response = (path in blobByPath)
? new Response(blobByPath[path], status.ok)
: new Response(empty, status.notFound);
return Promise.resolve(response);
};
}