ByteDance Interviewer: Пожалуйста, используйте JS для реализации контроля одновременных запросов Ajax

Promise опрос
ByteDance Interviewer: Пожалуйста, используйте JS для реализации контроля одновременных запросов Ajax

Я последнее время давно не вывожу статью, причина очень проста, я был очень занят в последнее время,

Честно говоря, в последнее время я был очень растерян. О технике, о жизни. Я также нашел много друзей на большой фабрике, чтобы поболтать, желая поделиться некоторыми идеями для дальнейшего развития. В нем также говорилось о собеседовании и о некоторых вопросах, которые будут заданы кандидату при приеме на работу. Просто у меня давно не было интервью, поэтому я выбрал несколько. В последнее время будет серия разборов некоторых вопросов интервью.

Сегодня это бит бит:

实现一个批量请求函数 multiRequest(urls, maxNum),要求如下:
• 要求最大并发数 maxNum
• 每当有一个请求返回,就留下一个空位,可以增加新的请求
• 所有请求完成后,结果按照 urls 里面的顺序依次打出

Я думаю, что многие студенты более или менее видели эту тему.Далее я постараюсь дать полный разбор этой темы шаг за шагом от места, разбора проблемы до финальной реализации.

Сцены

Предположим, есть такой сценарий: нужно отправить 30 асинхронных запросов, но по какой-то причине мы должны контролировать количество одновременных запросов в пределах 5, при этом также максимально быстро получить результаты ответа.

То, что должно быть сделано?

Сначала давайте разберемсяAjaxпоследовательный и параллельный.

Последовательная и параллельная реализация Ajax на основе Promise.all

Мы всегда основываемся наpromiseЧтобы инкапсулировать асинхронные запросы, это в основном для асинхронных запросов.

  • Последовательный: после завершения асинхронного запроса выполняется следующий запрос.
  • Параллельный: несколько асинхронных запросов выполняются одновременно.

путем определения некоторыхpromise实例для подробной демонстрации последовательного/параллельного режима.

сериал

var p = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('1000')
      resolve()
    }, 1000)
  })
}
var p1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('2000')
      resolve()
    }, 2000)
  })
}
var p2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('3000')
      resolve()
    }, 3000)
  })
}


p().then(() => {
  return p1()
}).then(() => {
  return p2()
}).then(() => {
  console.log('end')
})

Например, последовательный интерфейс будет выполнять соответствующие запросы интерфейса последовательно сверху вниз.

параллельно

Обычно, когда нам нужно убедиться, что код выполняется после многократной асинхронной обработки, мы будем использовать:

Promise.all(promises: []).then(fun: function);

Promise.allможет гарантировать,promisesвсе в массивеpromiseобъекты достигаютresolveстатус, выполнитьthenПерезвоните.

var promises = function () {
  return [1000, 2000, 3000].map(current => {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log(current)
      }, current)
    })
  })
}

Promise.all(promises()).then(() => {
  console.log('end')
})

Предел параллелизма Promise.all

Рассмотрим сценарий в это время: если вашpromisesКаждый объект массиваhttp请求, и таких объектов сотни тысяч.

Тогда произойдет то, что вы отправите сотни тысяч в одно мгновениеhttp请求, что может привести к накоплению большого количества стеков вызовов и вызвать переполнение памяти.

На этом этапе нам необходимо рассмотретьPromise.allСделайте ограничение параллелизма.

Promise.all并发限制Относится к параллельному выполнению в каждый моментpromiseКоличество фиксировано, а окончательный результат выполнения остается таким же, как и исходныйPromise.allПоследовательный.

Реализация темы

Анализ мыслей

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

Код

function multiRequest(urls = [], maxNum) {
  // 请求总数量
  const len = urls.length;
  // 根据请求数量创建一个数组来保存请求的结果
  const result = new Array(len).fill(false);
  // 当前完成的数量
  let count = 0;

  return new Promise((resolve, reject) => {
    // 请求maxNum个
    while (count < maxNum) {
      next();
    }
    function next() {
      let current = count++;
      // 处理边界条件
      if (current >= len) {
        // 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回
        !result.includes(false) && resolve(result);
        return;
      }
      const url = urls[current];
      console.log(`开始 ${current}`, new Date().toLocaleString());
      fetch(url)
        .then((res) => {
          // 保存请求结果
          result[current] = res;
          console.log(`完成 ${current}`, new Date().toLocaleString());
          // 请求没有全部完成, 就递归
          if (current < len) {
            next();
          }
        })
        .catch((err) => {
          console.log(`结束 ${current}`, new Date().toLocaleString());
          result[current] = err;
          // 请求没有全部完成, 就递归
          if (current < len) {
            next();
          }
        });
    }
  });
}