面试内容
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编程的能力。
希望能给大家一些帮助,祝我们都能马到成功!