FE-Interview icon indicating copy to clipboard operation
FE-Interview copied to clipboard

请实现如下的函数

Open lgwebdream opened this issue 5 years ago • 4 comments

/*
	可以批量请求数据,所有的 URL 地址在 urls 参数中,同时可以通过 max 参数控制请求的并发度,当所有请求结束之后,需要执行 callback 回调函数。发请求的函数可以直接使用 fetch 即可
*/

lgwebdream avatar Jul 06 '20 15:07 lgwebdream

扫描下方二维码,获取答案以及详细解析,同时可解锁800+道前端面试题。

lgwebdream avatar Jul 06 '20 15:07 lgwebdream

为了避免使用全局变量,我们首先使用闭包的形式

function request(urls, max, callback) {
  let len = urls.length;
  let currentUrlIndex = 0;
  let counter = 0;

  return function () {

    function batch() {
      while (currentUrlIndex < len) {
        if (counter <= max) {
          myFetch(urls[currentUrlIndex]);
          counter++;
        }
        currentUrlIndex++;

        if (counter === max) {
          counter = 0;
          console.log('执行了一个批量')
          break;
        }
      }
      doBatch();
    }
  
    function doBatch() {
      if (currentUrlIndex < len) {
        batch()
      } else {
        callback()
      }
    }

    doBatch();
  }
}

function myFetch(url) {
  console.log(url + '执行了')
}

function cb() {
  console.log('所有的都结束了')
}

var urls = ['url1', 'url2', 'url3', 'url4', 'url5', 'url6', 'url7']
var fn = request(urls, 4, cb);
fn();

// 优化一下,封装成类

function Request(urls, max, callback) {
  this.urls = urls;
  this.max = max;
  this.callback = callback;
  this.len = urls.length;
  this.currentUrlIndex = 0;
  this.counter = 0;
}

Request.prototype.batch = function (params) {
  while (this.currentUrlIndex < this.len) {
    if (this.counter < this.max) {
      myFetch(this.urls[this.currentUrlIndex]);
      this.counter++;
    }
    this.currentUrlIndex++;
    if (this.counter === this.max) {
      this.counter = 0;
      break;
    }
  }
  this.doBatch();
}

Request.prototype.doBatch = function () {
  if (this.currentUrlIndex < this.len) {
    this.batch()
  } else {
    this.callback()
  }
}

function myFetch(url) {
  console.log(url + '执行了')
}

function cb() {
  console.log('所有的都结束了')
}

var urls = ['url1', 'url2', 'url3', 'url4', 'url5', 'url6', 'url7']

var request = new Request(urls, 3, cb);
request.doBatch()

Neisun avatar Dec 30 '21 09:12 Neisun


const batchFetch = (urls = [], max = 3, callback) => {
  if (!callback) {
    throw new Error("Must set callback");
  }

  /**
   * @param urlsLenght 总请求数量
   * @param pendingFetchNums 进行中的请求数量
   * @param resolvedFetchNums 结束的请求数量
   * @param resolvedFetchs 保存请求结果
   */
  const urlsLenght = urls.length;
  let pendingFetchNums = 0;
  let lastFetchIndex = 0;
  let resolvedFetchs = new Map();

  const doBatch = () => {
    while (pendingFetchNums < max && lastFetchIndex < urlsLenght) {
      const url = urls[lastFetchIndex];
      pendingFetchNums++;
      lastFetchIndex++;

      console.log(
        `DEBUG 
        pendingFetchNums ${pendingFetchNums}
        lastFetchIndex: ${lastFetchIndex} 
        url: ${url}`
      );

      Promise.resolve(url).then(result => {
        pendingFetchNums--;
        resolvedFetchs.set(url, result);
        console.log(
          `Success get ${url} result: ${result}, current resolved urls size: ${resolvedFetchs.size}, all fetch length: ${urlsLenght}`
        );

        if (resolvedFetchs.size === urlsLenght) {
          const result = [...resolvedFetchs.values()];
          console.log(
            `All promise successed resolved, should do callback handle, callback result: `,
            result
          );
          return callback(result);
        } else {
          doBatch();
        }
      });
    }
  };

  doBatch();
};

保存了每个请求的结果,并在调用 callback 的时候一起入参 为了方便测试,用 Promise.resolve 模拟 fetch 可以自行修改下

image

gaohan1994 avatar Apr 28 '24 04:04 gaohan1994