23 строки кода реализуют функцию запроса на выборку с ограничением параллелизма.

интервью

Я видел вопрос интервью, когда я был в гостях у Наггетс на завтрак утром. Я видел, как большие парни внизу демонстрируют свои удивительные способности. У меня также зудели руки. Я взял свою механическую клавиатуру и набрал наугад, и обнаружил, что это занимает 30 строк кода, чтобы добиться этого. , кажется, это не очень скучно👻👻, заходите и листайте вниз вместе со мной😛

14.03.2019 Позднее добавление: я увидел ответ от большого парня @serialcoder после обеда и прямо окаменел от него. Я внимательно прочитал заголовок и обнаружил, что он действительно неправильный. Это было слишком небрежно 😢 Моя предыдущая реализация была множественной запросы одновременные, но не одновременно, а несколько одновременных сериалов. Поэтому я снова начал стучать, и на это ушло около получаса. Эта встреча должна быть правильной, а она заняла всего 23 строчки, поэтому название я тоже быстро сменила 😝

оригинальное название

Источник темы:Помните вопрос о внешнем интерфейсе, который контролирует количество параллелизма [вручную поддерживать очередь HTTP-запросов]

идеи

чтение вопросов

Проходя вопрос быстро, вы можете получить следующую ключевую информацию:(Виновник, читайте вопросы внимательно и не жадничайте 😢)

  1. пакетный запрос
  2. контролируемый параллелизм
  3. Все запросы заканчиваются, выполнятьcallback

решение проблем

  1. пакетный запрос

Чтобы добиться пакетных запросов, и не обязательно инициировать запросы по порядку (если нужно хранить их в очереди по порядку, а в приоритетной очереди, то можно хранить в приоритетной очереди), так что здесь мы можем их хранить в массиве, а затем пройти, взять каждый элемент в номере и выбросить егоfetchвызывать.

  1. контролируемый параллелизм

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

Трудность этого вопроса здесь, и именно этот шаг был неправильным раньше. Контролируя количество параллелизма, каждый раз, когда запрос завершается и инициируется новый запрос. Рекурсивный метод по-прежнему используется, но на этот раз мы добавляем очередь запросов, а дальше нам нужно только поддерживать эту очередь, добавлять ее каждый раз, когда мы инициируем запрос, и выбрасывать ее в конце, таким образом достигая контроля параллелизма.

  1. Все запросы заканчиваются, выполнятьcallback

Поскольку это асинхронный запрос, мы не можем надеяться, что нормальный порядок установки будет выполнен после вызова функции, но каждый разfetchОн будет вызываться, когда будет возвращаемый результатthenилиcatch, мы можем судить, пуст ли массив запросов в это время, чтобы узнать, все ли они были вызваны

написать вопрос

Код из предыдущего ❌

(Следующее можно игнорировать, это неправильное решение, написанное ранее)

На этом этапе нечего сказать, просто засучите рукава и постучите~

function handleFetchQueue(urls, max, callback) {
  const requestArr = [];
  urls.forEach((item, idx) => {
    const i = Math.floor(idx / max);
    if (requestArr[i]) {
      requestArr[i].push(item)
    } else {
      requestArr[i] = [item]
    }
  });

  const handleSubRequests = (subReqs) => {
    const results = [];
    subReqs.forEach(req => {
      fetch(req).then(res => {
        if (results.push(res) === max) {
          if (requestArr.length < 1) {
            'function' === typeof callback && callback(results)
          } else {
            handleSubRequests(requestArr.shift(), requestArr, max)
          }
        }
      }).catch(e => {
        results.push(e)
      })
    })
  };
  handleSubRequests(requestArr.shift())
}

Вот две маленькие хитрости, о которых стоит упомянуть:

  • пройти черезMath.floor(idx / max)Мы можем легко поместить каждый элемент в правильный подмассив.
  • Эффективно используйте возвращаемое значение массива:results.push(res)Верните длину массива, просто используйте его напрямую

Прикрепите полный тестовый код:

function handleFetchQueue(urls, max, callback) {
  const requestArr = [];
  urls.forEach((item, idx) => {
    const i = Math.floor(idx / max);
    if (requestArr[i]) {
      requestArr[i].push(item)
    } else {
      requestArr[i] = [item]
    }
  });

  const handleSubRequests = (subReqs) => {
    const results = [];
    subReqs.forEach(req => {
      fetch(req).then(res => {
        if (results.push(res) === max) {
          if (requestArr.length < 1) {
            'function' === typeof callback && callback(results)
          } else {
            handleSubRequests(requestArr.shift(), requestArr, max)
          }
        }
      }).catch(e => {
        results.push(e)
      })
    })
  };
  handleSubRequests(requestArr.shift())
}


const urls = Array.from({length: 10}, (v, k) => k);

const fetch = function (idx) {
  return new Promise(resolve => {
    console.log(`start request ${idx}`);
    // 模拟请求时间
    const timeout = parseInt(Math.random() * 1e4);
    setTimeout(() => {
      console.log(`end request ${idx}`);
      resolve(idx)
    }, timeout)
  })
};

const max = 4;

const callback = () => {
  console.log('run callback');
};

handleFetchQueue(urls, max, callback);

Поскольку я работал в Node, (lan) ленивый (ai) должен был (fa) потерять (zuo) браузер для запуска, поэтому я смоделировал случайныйfetchфункция.

✅ код

function handleFetchQueue(urls, max, callback) {
  const urlCount = urls.length;
  const requestsQueue = [];
  const results = [];
  let i = 0;
  const handleRequest = (url) => {
    const req = fetch(url).then(res => {
      const len = results.push(res);
      if (len < urlCount && i + 1 < urlCount) {
        requestsQueue.shift();
        handleRequest(urls[++i])
      } else if (len === urlCount) {
        'function' === typeof callback && callback(results)
      }
    }).catch(e => {
      results.push(e)
    });
    if (requestsQueue.push(req) < max) {
      handleRequest(urls[++i])
    }
  };
  handleRequest(urls[i])
}

❌Код и ✅Сравнение тестов кода

Сначала посмотрите на ошибку, вы можете обнаружить, что она выполняется последовательно:

Глядя на правильный код, это параллелизм:

Вставьте полный код:

function handleFetchQueue(urls, max, callback) {
  const urlCount = urls.length;
  const requestsQueue = [];
  const results = [];
  let i = 0;
  const handleRequest = (url) => {
    const req = fetch(url).then(res => {
      console.log('当前并发: '+requestsQueue);
      const len = results.push(res);
      if (len < urlCount && i + 1 < urlCount) {
        requestsQueue.shift();
        handleRequest(urls[++i])
      } else if (len === urlCount) {
        'function' === typeof callback && callback(results)
      }
    }).catch(e => {
      results.push(e)
    });
    if (requestsQueue.push(req) < max) {
      handleRequest(urls[++i])
    }
  };
  handleRequest(urls[i])
}


const urls = Array.from({length: 10}, (v, k) => k);

const fetch = function (idx) {
  return new Promise(resolve => {
    console.log(`start request ${idx}`);
    const timeout = parseInt(Math.random() * 1e4);
    setTimeout(() => {
      console.log(`end request ${idx}`);
      resolve(idx)
    }, timeout)
  })
};

const max = 4;

const callback = () => {
  console.log('run callback');
};


handleFetchQueue(urls, max, callback);

Суммировать

пройти черезчтение вопросов,решение проблеминаписать вопросТри шага (самостоятельно созданные 😛) могут сделать наше мышление очень ясным и очень простым для решения вопросов на собеседовании.

На решение проблемы ушло 20 минут, но статья писалась час, что было непросто.Если вы считаете, что это хорошо, пожалуйста, дайте мне аплодисменты~~