业务场景代码✅
统计前端请求耗时
答案
方法类别 | 技术手段/工具 | 实现方式简述 | 代码示例/要点 | 优缺点说明 |
---|---|---|---|---|
初级 | fetch + 时间戳 | 请求前后用 performance.now() 记录时间,计算差值即为耗时 | const start = performance.now(); fetch(...).then(() => { const end = performance.now(); ... }) | 简单易用,受主线程阻塞影响,统计不一定准确 |
初级 | XMLHttpRequest + 时间戳 | 请求前后用 performance.now() 记录时间,事件回调中计算耗时 | xhr.onload = function () { ... } | 同上 |
初级 | axios 拦截器 | 请求拦截器记录开始时间,响应拦截器记录结束时间,计算耗时 | axios.interceptors.request.use(...) | 适合 axios 用户,易集成 |
进阶 | Performance API | 用 performance.timing 或 performance.getEntriesByType('resource') 获取详细性能数据 | performance.timing.responseEnd - performance.timing.requestStart | 能统计页面各阶段耗时,适合分析整体性能 |
高级 | Web Worker | 在 Worker 线程中发起请求并统计耗时,主线程不被阻塞 | Worker 文件中用 performance.now() 统计,主线程通过 postMessage 获取结果 | 不阻塞主线程,适合高并发或复杂场景 |
说明:
- 初级手段适合简单场景,易于实现,但统计结果可能受主线程阻塞影响。
- 进阶和高级手段能获得更准确或更详细的性能数据,适合对性能要求较高的业务场景。
cookie 解析函数
JS 执行 100 万个任务, 如何保证浏览器不卡顿?
答案
方法 | 实现方式简述 | 代码要点/示例 | 优缺点说明 |
---|---|---|---|
Web Worker | 将任务转移到 Worker 线程,主线程不阻塞 | new Worker('worker.js') | 彻底避免主线程卡顿,适合大量计算 |
requestAnimationFrame | 将大任务分割为小块,每帧处理一部分,保持页面流畅 | requestAnimationFrame(fn) | 简单易用,适合与渲染相关的分步任务 |
动态 chunkSize | 动态调整每帧处理任务量,保证每帧耗时不超限 | 根据 performance.now() 动态调整 | 进一步优化不卡顿体验,适应不同设备性能 |
requestIdleCallback | 在浏览器空闲时分批处理任务,避免影响主线程响应 | requestIdleCallback(fn) | 最大化利用空闲时间,兼容性需注意 |
- Web Worker 适合 CPU 密集型任务,完全不阻塞主线程。
- requestAnimationFrame/requestIdleCallback 适合与 UI 相关或需分步处理的任务。
- 动态 chunkSize 可进一步提升分块处理的平滑性。
- 可根据业务场景组合使用上述方法。
如何封装一个请求,让其多次调用的时候,实际只发起一个请求的时候,返回同一份结果
答案
最优解:利用 Promise 缓存,将请求结果复用,避免重复请求。
实现思路如下:
- 用一个对象(如 Map)缓存每个请求的 Promise。
- 多次调用时,如果缓存中已有对应的 Promise,直接返回该 Promise。
- 只有第一次会真正发起请求,后续调用复用同一个 Promise,拿到同一份结果。
示例代码:
const requestCache = new Map()
function singletonRequest (url, options) {
const key = url + JSON.stringify(options || {})
if (requestCache.has(key)) {
return requestCache.get(key)
}
const reqPromise = fetch(url, options).then(res => res.json())
requestCache.set(key, reqPromise)
return reqPromise
}
// 用法
singletonRequest('/api/data').then(console.log)
singletonRequest('/api/data').then(console.log)
// 只会发起一次请求,所有调用拿到同一份结果
说明:
- 这种方式简单高效,适合幂等请求(如 GET)。
- 若需支持请求失败重试或缓存失效,可在 Promise reject 时清理缓存或设置过期策略。
- 也可用闭包变量实现,无需全局变量。
使用 Promise 实现一个异步流量控制的函数(限制并发数)
集合运算
答案
可以使用 ES6 的 Set 数据结构来实现数组交集。
首先,将一个数组转化为 Set,然后遍历另一个数组,将数组中存在于 Set 中的元素存入结果数组中。
以下是一个示例代码:
function intersection (nums1, nums2) {
const set1 = new Set(nums1)
const res = []
for (const num of nums2) {
if (set1.has(num)) {
res.push(num)
}
}
return res
}
使用示例:
const nums1 = [1, 2, 2, 1]
const nums2 = [2, 2]
console.log(intersection(nums1, nums2)) // [2]
该算法的时间复杂度为 O(m+n),其中 m 和 n 分别为两个数组的长度。
限制并发的 Promise
答案
async function promiseLimit<T> (
promises: (() => Promise<T>)[],
limit: number
): Promise<T[]> {
const results: T[] = []
const executing: Promise<void>[] = []
for (const [index, promiseFunc] of promises.entries()) {
const p = Promise.resolve()
.then(() => promiseFunc())
.then(result => {
results[index] = result
})
.finally(() => {
const i = executing.indexOf(p)
if (i !== -1) executing.splice(i, 1)
})
executing.push(p)
if (executing.length >= limit) {
await Promise.race(executing)
}
}
await Promise.all(executing)
return results
}