我在一本书中遇到一个似乎无法弄清的问题。不幸的是,我没有实时链接,所以如果有人可以在理论上帮助我,我将非常感激。

过程:


我从fetch调用中得到了一个字符串代码数组(["abcde", "fghij", "klmno", "pqrst"])。
我想用每个字符串代码调用链接。
例:


fetch('http://my-url/abcde').then(res => res.json()).then(res => res).catch(error => new Error(`Error: ${error}`)); // result: 12345
fetch('http://my-url/fghij').then(res => res.json()).then(res => res).catch(error => new Error(`Error: ${error}`)); // result: 67891
...etc



如图所示,每个电话都会给我一个电话号码。
我需要获得最大的5个数字,并获取其传入的字符串代码,然后再进行一次调用。


“ abcde” => 1234

“ fghij” => 5314

“ klmno” => 3465

“ pqrst” => 7234
fetch('http://my-url/pqrst').then(res => res.json()).then(res => res).catch(error => new Error(`Error: ${error}`));


我试过的

let codesArr = []; // array of string codes
let promiseArr = []; // array of fetch using each string code in `codesArr`, meant to be used in Promise.all()
let codesObj = {}; // object with string code and its afferent number code gotten from the Promise.all()

fetch('http://my-url/some-code')
.then(res => res.json())
.then(res => codesArr = res) // now `codesArr` is ["abcde", "fghij", "klmno", "pqrst"]
.catch(error => new Error(`Error: ${error}`);

for(let i = 0; i < codesArr.length; i++) {
      promiseArr.push(
         fetch(`http://my-url/${codesArr[i]}`)
            .then(res => res.text())
            .then(res => {
               codesObj[codesArr[i]] = res;
                        // This is to get an object from which I can later get the highest number and its string code. Like this:
                        // codesObj = {
                        //  "abcde": 12345,
                        //  "fghij": 67891
                        // }
               })
              .catch(error => new Error(`Error: ${error}`));
               // I am trying to make an array with fetch, so that I can use it later in Promise.all()
}

Promise.all(promiseArray) // I wanted this to go through all the fetches inside the `promiseArr` and return all of the results at once.
      .then(res => {
         for(let i = 0; i < res.length; i++) {
            console.log(res[i]);
            // this should output the code number for each call (`12345`, `67891`...etc)
            // this is where I get lost
         }
      })



到目前为止,我的方法存在的问题之一似乎是发出太多请求,并且出现429错误。有时我会得到正确的数字代码,但不是很常见。

最佳答案

就像您已经发现429一样,意味着您发送了太多请求:


  429 Too Many Requests
  
  用户在给定的时间内发送了太多请求(“
  限制”)。
  
  响应表示应包括详细说明
  条件,并且可以包含一个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>

  
  请注意,此规范未定义原始服务器的方式
  识别用户,也不能识别请求计数。例如,
  限制请求速率的原始服务器可以基于
  整个服务器中每个资源的请求数,
  甚至是一组服务器之间。同样,它可以识别用户
  通过其身份验证凭据或有状态Cookie。
  
  状态码为429的响应不得由高速缓存存储。


要解决此问题,您应该在设定的时间内减少请求的数量。您应该延迟迭代代码,使请求间隔几秒钟。如果在429响应中未指定,则必须使用反复试验的方法来找到有效的延迟。在下面的示例中,我将它们隔开了2秒(2000毫秒)。

可以通过使用setTimeout()稍后执行一些代码,使用combine this with a Promise创建sleep函数来完成。然后,在每个特定时间使用async function睡眠进行每次迭代。

例如:



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);
  };
}

08-08 04:45