跳到主要内容

业务场景代码✅

统计前端请求耗时

答案
方法类别技术手段/工具实现方式简述代码示例/要点优缺点说明
初级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 APIperformance.timingperformance.getEntriesByType('resource') 获取详细性能数据performance.timing.responseEnd - performance.timing.requestStart能统计页面各阶段耗时,适合分析整体性能
高级Web Worker在 Worker 线程中发起请求并统计耗时,主线程不被阻塞Worker 文件中用 performance.now() 统计,主线程通过 postMessage 获取结果不阻塞主线程,适合高并发或复杂场景

说明:

  • 初级手段适合简单场景,易于实现,但统计结果可能受主线程阻塞影响。
  • 进阶和高级手段能获得更准确或更详细的性能数据,适合对性能要求较高的业务场景。

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
}
48%