6

JavaScript 面试题一则:有限并行

 3 years ago
source link: https://zhuanlan.zhihu.com/p/360193435
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JavaScript 面试题一则:有限并行

上海和今信息科技有限公司 技术总监

这是一道我常用的面试题,大部分面试者回答地都不太好,所以今天写一写。

当我们有一堆异步任务需要执行时,常见的写法为

async function fetch (url) {
// 请求一个URL并返回其响应,省略其实现
}

// 写法 1
const results = await Promise.all(urls.map(url => fetch(url)));

// 写法 2
const results = [];
for (let i = 0; i < urls.length; i += 1) {
    results.push(await fetch(urls[i]))
}

这是一个开放性问题,我一般会问被试者这两个写法会有什么问题,如何优化。

这里给一下我的答案。

写法 1 是完全并行的,当 urls 数组很大时,大量的 Promise 和函数上下文的创建可能会导致性能问题。

而写法 2 是完全串行的,问题在于并行度低,效率差。

所以可以引入一个工具函数 parallel

  async function parallel(jobs, fn, workerCount = 5) {   
    const ret = new Array(jobs.length);

    let cursor = 0;
    async function worker(workerId) {
      let currentJob;
      while (cursor < jobs.length) {
        try {
          currentJob = cursor;
          cursor += 1;
          ret[currentJob] = await fn(jobs[currentJob]);
        } catch (e) {
          console.log(`worker: ${workerId} job: ${currentJob}`, e);
        }
      }
    }

    const workers = [];

    for (let i = 0; i < workerCount && i < jobs.length; i += 1) {
      workers.push(worker(i));
    }

    await Promise.all(workers);

    return ret;
  }

则上面的写法改为

const results = await parallel(urls, (url) => fetch(url), 5)

可以控制并行的规模,并且用法上和 map 写法类似。

parallel 的实现利用了 JavaScript 基于事件循环的单线程异步和 run to completion 语义,顺便考察了常用的异步API的使用,在白板面试的条件下完成需要比较强的代码功底。

P.S. 这个写法很容易联想到其它语言的多线程实现,比如 Python/C++/ Java,可以横向对照。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK