去哪儿面经新鲜出炉

0 阅读4分钟

面试内容

1. 自我介绍

2. AI项目相关

1.为什么选Claude Code?
2.为了解决AI幻觉相关问题做了哪些努力?
3 如何落地的?

3. C端性能优化方案讨论

结合项目回答的,这里就不展开了

4.手撕题

题目

// 封装一个请求方法

const req1 = async () => {console.log('req1')}
const req2 = async () => {console.log('req2')}

const req = [req1, req2,........reqn]; req.length >= 10 
const limit = 10

// 并发请求池处理

// const asyncPool = (reqs, limit = 10) => {

  


}

  


// 返回 result [res1, res2,......error]

实现1:

/**
 * 并发请求池实现思路:
 * 1) 维护一个共享下标 nextIndex,所有 worker 从该下标领取下一个任务。
 * 2) 同时启动 workerCount 个 worker(不超过 limit),控制并发上限。
 * 3) 每个任务执行结果写入 results[currentIndex],保证返回顺序与入参顺序一致。
 * 4) 单个任务失败不抛出到整体,错误对象写入对应位置,最终统一返回结果数组。
 */
const asyncPool = async (reqs, limit = 10) => {
  if (!Array.isArray(reqs)) {
    throw new TypeError("reqs must be an array");
  }

  if (reqs.length === 0) {
    return [];
  }

  const safeLimit = Math.max(1, Number(limit) || 1);
  const results = new Array(reqs.length);
  let nextIndex = 0;

  const worker = async () => {
    // 每个 worker 不断领取新任务,直到队列消费完成
    while (nextIndex < reqs.length) {
      const currentIndex = nextIndex;
      nextIndex += 1;

      const req = reqs[currentIndex];
      if (typeof req !== "function") {
        results[currentIndex] = new TypeError("each item in reqs must be a function");
        continue;
      }

      try {
        // 按原始索引写入结果,确保返回顺序稳定
        results[currentIndex] = await req();
      } catch (error) {
        // 不中断整体执行,失败项写入错误对象
        results[currentIndex] = error;
      }
    }
  };

  const workerCount = Math.min(safeLimit, reqs.length);
  // 并发启动固定数量 worker
  await Promise.all(new Array(workerCount).fill(null).map(() => worker()));

  return results;
};

module.exports = {
  asyncPool,
};

// 测试
if (require.main === module) {
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

  const reqs = [
    async () => {
      await delay(300);
      return "res-1";
    },
    async () => {
      await delay(100);
      return "res-2";
    },
    async () => {
      await delay(200);
      throw new Error("err-3");
    },
    async () => {
      await delay(80);
      return "res-4";
    },
    async () => {
      await delay(120);
      return "res-5";
    },
    async () => {
      await delay(160);
      throw new Error("err-6");
    },
    async () => {
      await delay(90);
      return "res-7";
    },
    async () => {
      await delay(70);
      return "res-8";
    },
    async () => {
      await delay(140);
      return "res-9";
    },
    async () => {
      await delay(60);
      return "res-10";
    },
  ];

  asyncPool(reqs, 3).then((result) => {
    const view = result.map((item) => (item instanceof Error ? item.message : item));
    console.log("result:", view);
  });
  // 实际输出
  result: [
  'res-1', 'res-2',
  'err-3', 'res-4',
  'res-5', 'err-6',
  'res-7', 'res-8',
  'res-9', 'res-10'
]
}

实现2 class写法,我采用的写法

/**
 * 并发请求池思路:
 * 1) 使用 nextIndex 作为共享游标分发任务,多个 worker 竞争领取下一个请求。
 * 2) worker 数量固定为 min(limit, reqs.length),从而限制最大并发数。
 * 3) 每个请求的结果按原下标写回 results,保证返回顺序稳定。
 * 4) 单个请求失败不影响整体流程,错误对象写入对应位置后继续执行。
 */
class AsyncPool {
  constructor(reqs, limit = 10) {
    if (!Array.isArray(reqs)) {
      throw new TypeError("reqs must be an array");
    }

    this.reqs = reqs;
    this.limit = Math.max(1, Number(limit) || 1);
    this.results = new Array(reqs.length);
    this.nextIndex = 0;
  }

  async runTask() {
    // 当前 worker 持续拉取任务,直到所有任务分发完成
    while (this.nextIndex < this.reqs.length) {
      const currentIndex = this.nextIndex;
      this.nextIndex += 1;

      const req = this.reqs[currentIndex];
      if (typeof req !== "function") {
        this.results[currentIndex] = new TypeError("each item in reqs must be a function");
        continue;
      }

      try {
        // 按请求原始位置回填结果
        this.results[currentIndex] = await req();
      } catch (error) {
        // 失败不抛出到外层,记录错误并继续后续任务
        this.results[currentIndex] = error;
      }
    }
  }

  async run() {
    if (this.reqs.length === 0) {
      return [];
    }

    // 创建固定数量 worker,控制并发上限
    const workerCount = Math.min(this.limit, this.reqs.length);
    const workers = new Array(workerCount).fill(null).map(() => this.runTask());
    await Promise.all(workers);
    return this.results;
  }
}

const asyncPool = (reqs, limit = 10) => {
  const pool = new AsyncPool(reqs, limit);
  return pool.run();
};

module.exports = {
  asyncPool,
  AsyncPool,
};

if (require.main === module) {
  const createReq = (id, delay, shouldReject = false) => async () => {
    await new Promise((resolve) => setTimeout(resolve, delay));
    if (shouldReject) {
      throw new Error(`err-${id}`);
    }
    return `res-${id}`;
  };

  const reqs = [
    createReq(1, 1000),
    createReq(2, 1200),
    createReq(3, 800),
    createReq(4, 600, true),
    createReq(5, 500),
    createReq(6, 300),
    createReq(7, 700),
    createReq(8, 400, true),
    createReq(9, 900),
    createReq(10, 200),
  ];

  asyncPool(reqs, 3).then((result) => {
    console.log("result:", result);
  });
  // 结果
  result: [
  'res-1',
  'res-2',
  'res-3',
  Error: err-4
      at /Users/xieqi/Desktop/Demo/pool1.js:58:13
      at async AsyncPool.runTask (/Users/xieqi/Desktop/Demo/pool1.js:25:38)
      at async Promise.all (index 2)
      at async AsyncPool.run (/Users/xieqi/Desktop/Demo/pool1.js:39:5),
  'res-5',
  'res-6',
  'res-7',
  Error: err-8
      at /Users/xieqi/Desktop/Demo/pool1.js:58:13
      at async AsyncPool.runTask (/Users/xieqi/Desktop/Demo/pool1.js:25:38)
      at async Promise.all (index 0)
      at async AsyncPool.run (/Users/xieqi/Desktop/Demo/pool1.js:39:5),
  'res-9',
  'res-10'
]
}

自己手撕了20分钟写了个大概,后面面试官说还有什么改进的空间吗?
给你5分钟,可以使用AI Coding。

5. 反问环节

问了AI冲击下,去哪儿更倾向于哪种做法?
1.借助AI,模糊前后端开发边界,统一为软件开发工程师;
2.看中AI深度开发能力,希望前端同学往AI开发方向发展(✅)。

总结

面试全程无八股,主要针对项目和AI(创新开发);
有手撕代码环节,最后还允许使用AI
整体面试时长1小时20分钟,很喜欢这种面试,感觉很能考察候选人真实能力。

最近面试有感

最近面试,好像确实很少考察八股了,更看重项目经验和架构能力,同时AI一定会被问到。
大厂的话手撕是一定有的,也是要自己写的,只不过我遇到的手撕基本上都是和业务有关联的,很少纯leetcode那种算法题,最后就是有的厂会借助编程题顺带考察一下使用AI编程的能力。
希望能给大家一些帮助,祝我们都能马到成功!