Как управлять интерфейсом для одновременной отправки 10 одинаковых запросов?

внешний интерфейс опрос

Описывается следующим образом

  • Отправляйте несколько одинаковых запросов одновременно. Если первый запрос будет успешным, остальные запросы не будут отправлены, а успешный результат будет возвращен как оставшийся запрос.
  • Если первый запрос не удался, то будет отправлен запрос с номером 2. Если запрос будет успешным, остальные запросы не будут отправлены, а успешный результат будет возвращен как оставшийся запрос.
  • Если второй запрос не удался, то отправьте запрос под номером 3. Если запрос будет успешным, остальные запросы не будут отправлены, а успешный результат будет возвращен как оставшийся запрос
  • ...Эта рекурсия, пока в худшем случае не потребуется отправить последний запрос

Параллелизм: запрос интерфейса все еще находится на рассмотрении, и тот же запрос отправляется через короткий промежуток времени.

async function fetchData (a)  {
    const data = await fetch('//127.0.0.1:3000/test')
    const d = await data.json();
    console.log(d);
    return d;
}

fetchData(2) // 编号 1
fetchData(2) // 2
fetchData(2) // 3
fetchData(2) // 4
fetchData(2) // 4
fetchData(2) // 5
fetchData(2)
fetchData(2)

старая версияcachedAsync

я использовал раньшеvueФункция cache кеширует успешные запросы, реализация такая. последующийcachedAsyncТолько успешные запросы будут кэшироваться, и если они не пройдут, новый запрос будет получен напрямую. Но если это описанный выше параллельный сценарий, поскольку один и тот же запрос не может попасть в кеш, возникнет проблема с отправкой трех запросов подряд, и этот параллельный сценарий не может быть обработан.

const cachedAsync = function(fn) {
    const cache = Object.create(null);
    return async str => {
        const hit = cache[str];
        if (hit) {
            return hit;
        }
        // 只缓存成功的Promise, 失败直接重新请求
        return (cache[str] = await fn(str));
    };
};
const fetch2 = cachedAsync(fetchData)
fetch2(2);
fetch2(2);
fetch2(2);

Расширенная версия

В первую очередь необходимо кэширование, затем нам нужно только разобраться, как контролировать параллелизм. Может быть такая идея

  • Каждый запрос возвращает новый промис, а время выполнения эксектора промиса сохраняется в очереди.
  • Когда длина очереди равна 1, выполнить запрос, если запрос успешен, затем пройти по эксектору в очереди, получить результат запроса и разрешить.
  • Если запрос не выполнен, то этот Promise Reject отбрасывается, и стек помещается. Затем рекурсивно вызовитеnext
  • пока очередь exector не будет очищена
  const cacheAsync = (promiseGenerator, symbol) => {
    const cache = new Map();
    const never = Symbol();
    return async (params) => {
      return new Promise((resolve, reject) => {
      // 可以提供键值
        symbol = symbol || params;
        let cacheCfg = cache.get(symbol);
        if (!cacheCfg) {
          cacheCfg = {
            hit: never,
            exector: [{ resolve, reject }],
          };
          cache.set(symbol, cacheCfg);
        } else {
          // 命中缓存
          if (cacheCfg.hit !== never) {
            return resolve(cacheCfg.hit)
          }
          cacheCfg.exector.push({ resolve, reject });
        }

        const { exector } = cacheCfg;
        
        // 处理并发,在请求还处于pending过程中就发起了相同的请求
        // 拿第一个请求
        if (exector.length === 1) {
          const next = async () => {
            try {
              if (!exector.length) return;
              const response = await promiseGenerator(params);
              // 如果成功了,那么直接resolve掉剩余同样的请求
              while (exector.length) { // 清空
                exector.shift().resolve(response); 
              }
              // 缓存结果
              cacheCfg.hit = response;
            } catch (error) {
              // 如果失败了 那么这个promise的则为reject
              const { reject } = exector.shift();
              reject(error);
              next(); // 失败重试,降级为串行
            }
          };
          next();
        }
      });
    };
  };

Тестовый кешАсинхронный

Сценарии для тестирования

  • Интерфейс запроса случайным образом завершается успешно или неудачно
  • Успешный ожидаемый результат, остальные запросы не будут выдаваться
  • Повторите попытку в случае сбоя, затем отправьте следующий запрос

Быстро собрать сервер:

const koa = require("koa");
const app = new koa();

function sleep(seconds) {
 return new Promise((resolve, reject) => {
   setTimeout(resolve, seconds);
 });
}

app.use(async (ctx, next) => {
 if (ctx.url === "/test") {
   await sleep(200);

   const n = Math.random();
   // 随机挂掉接口
   if (n > 0.8) {
       ctx.body = n;
   } else {
       ctx.status = 404
       ctx.body = ''
   }
   next();
 }
});

app.listen(3000, "127.0.0.1", () =>
 console.log("listening on 127.0.0.1:3000")
);

клиент


  var fetch2 = cacheAsync(fetchData, "test2");

  async function fetchData(a) {
    const data = await fetch("//127.0.0.1:3000/test");
    const d = await data.json();
    console.log(d);
    return d;
  }
   // 并发6个相同的请求
  console.log(fetch2(2));
  console.log(fetch2(2));
  console.log(fetch2(2));
  console.log(fetch2(2));
  console.log(fetch2(2));
  console.log(fetch2(2));

Посмотрите на результаты теста и обновите следующую страницу

В первый раз очень повезло, запрос прошел успешно в первый раз, и был отправлен только один запрос

image.png

Второй тест - неудача, последний запрос прошел успешно, и это тоже худший сценарий.

image.png

Третий тест, запрос успешно выполнен в третий раз

image.png

тестовый кешАктивно запросить в консолиfetch2Успешный удар.

image.png

Судя по результатам теста, это правильно, в соответствии со сценариями параллелизма и кэширования. Некоторые люди спросят, например, почему вы хотите кэшировать интерфейсы. Когда вы вводите поиск по ключевому слову, событие ввода отслеживается.При добавлении или удалении ключевых слов будет сцена с теми же параметрами запроса.На данный момент это соответствует методу анти-тряски + интерфейс кеширование интерфейса. При обнаружении одного и того же ключевого слова извлекайте предыдущий кеш напрямую.

намекать

Поскольку этот кеш является методом закрытия, обновление кеша страницы также не удается. Однако я думаю, что это так, потому что в большинстве случаев обновление страницы приводит к сбросу состояния.Если вы хотите сохранить его, лучше сохранить его в локальном хранилище.

github-demo