网站空间 更换,公司网站后台如何上传视频,百度快速收录入口,做一个像美团的网站需要多少钱一、背景介绍
这是一个基于 axios 实现的批量任务调度管理器的 demo。它使用了axios、promise 等多种技术和原理来实现批量处理多个异步请求#xff0c;并确保所有请求都能正确处理并报告其状态。
假设有一个场景#xff1a;有一个任务列表#xff0c;有单个任务的处理功能…一、背景介绍
这是一个基于 axios 实现的批量任务调度管理器的 demo。它使用了axios、promise 等多种技术和原理来实现批量处理多个异步请求并确保所有请求都能正确处理并报告其状态。
假设有一个场景有一个任务列表有单个任务的处理功能但是用户提出需要增加批量处理任务的需求那么如果 20 个任务30 个任务我们显然可以集成在一次请求里无非就是服务器处理的压力变大时间慢一点但是如果越来越多的任务用户要等待很久后端为支持这些只能改成异步处理等待时间就很不可控了。并且在这个过程中很有可能对正在排队的任务进行其他处理会发生一些不必要的“错误”。所以提出前端对这些任务“批量处理”并且对这些任务处理时可以先对任务进行过滤和去重等统计并发请求的状态并展示最后实现了一个简单的【批量任务调度管理器 demo】。
这个功能还略显青涩所以后续又进行了优化和完善最近有新的想法借助第三方库比如使用类似p-limit或 p-queue 的任务队列库来管理批量任务。这些库提供了更高级的功能如任务调度、重试、延迟执行等这些待实验。
二、功能介绍
这个 demo 实现了以下功能
批量处理用户可以一次性提交多个任务这些任务会被批量处理将请求任务分成固定大小groupSize的批次进行处理以避免同时发送过多请求导致服务器压力过大或浏览器资源耗尽。并发控制通过递归调用 requestFunction 和控制 groupSize实现了对并发请求数量的控制。在每次请求完成后再启动新的请求确保同时运行的请求数量保持在合理范围内。有优化过滤和去重使用 filter 和 findIndex 方法对 tableData 进行过滤和去重确保每个任务只被处理一次也可以自定义其他处理方式。状态管理计数successCount 和 errorCount处理结果使用 ElMessage 和 ElNotification 进行用户提示反馈批量操作的结果。异步操作和错误处理通过 axios 库发送异步 HTTP 请求使用 then, catch 和 finally 方法处理请求结果和错误并在出错时记录错误信息。有优化
三、功能代码
// 批量处理多个任务
const batchOperation (title, operationType, axiosConfig, shouldFilterList) {startLoading() // 可忽略自定义的加载动画const groupSize 5 // 分组每组五个请求let successCount 0 // 成功的请求let errorCount 0 // 失败的请求let errorMessages [] // 请求失败时的错误信息记录// 过滤得到需要发生请求的任务列表const requestTaskList shouldFilterList? tableData.value.filter((item, index, arr) arr.findIndex((val) val.id item.id) index): tableData.valueif (requestTaskList.length 0) {ElMessage.error(没有可操作的任务)stopLoading()return}// 处理请求后的成功或者失败const handleResponse (error) {if (error) {errorCounterrorMessages.push(error)} else {successCount}if (successCount errorCount requestTaskList.length) {stopLoading()const message errorCount 0? 共 ${requestTaskList.length} 个任务全部处理成功。: 共 ${requestTaskList.length} 个任务${successCount} 个处理成功${errorCount} 个处理失败。ElNotification({title: ${title}结果,message,type: errorCount 0 ? success : warning})if (errorCount 0) {console.error(处理失败的任务, errorMessages)}}}//发送请求--借助递归const requestFunction () {// isUnmount是控制是否卸载当前组件if (isUnmount.value || nowIndex requestTaskList.length) {return}const row requestTaskList[nowIndex]const params { ...row } // 自定义传参axios.request({ url: axiosConfig.url, method: axiosConfig.method, params }).then((res) {if (res.data.code 200) {handleResponse(null)} else {handleResponse(res.data.message)}}).catch(handleResponse).finally(requestFunction)}let nowIndex 0for (let i 0; i groupSize; i) {requestFunction()}
}// 批量处理
const batchAll () {batchOperation(标题,batch, //处理参数的标识{url: /batch-task,method: get,params: { task_id: , id: }},true // 是否过滤列表)
}// 可忽略
const startLoading () {// 显示加载动画或状态containerLoading.value true
}const stopLoading () {// 隐藏加载动画或状态containerLoading.value false
}// 因为是vue,所以在组件卸载之前取消所有未完成的请求----可忽略
onBeforeUnmount(() {isUnmount.value true
})以上功能能实现的是上述阐述的功能也算一个简单的批量任务调度管理器但是还有很多优化方向
错误信息收集和展示 当前的错误处理仅记录了错误计数。可以改进为记录详细的错误信息并在批量操作完成后展示这些错误信息帮助用户理解哪些任务失败了以及原因这个看需求。动态并发控制 使用动态并发控制根据当前系统负载或网络状况调整并发请求数量进一步优化性能和稳定性这个需要依靠工具库完成。任务重试机制 为失败的请求添加重试机制例如在某个请求失败后可以重试一定次数以提高成功率这个看需求。Promise.all 优化 使用 Promise.all 处理每一批次的请求而不是递归调用 requestFunction可以使代码更简洁并减少递归带来的栈深度问题这个现在就可以优化。
四、功能优化
现在做的是基于 promise.all 的优化。我们要知道promise 本来就是 JS 有的一个 API那么当时我为何不考虑它呢还是我当时没有考虑到。 Axios 是基于 Ajax 和 Promise 封装本来就可以利用 Promise 来更好的管控请求回调嵌套造成的回调地狱如果不加控制查了一下那种递归确实可能会造成调用栈过深出现栈溢出问题我还没有遇到过不知道这种情况是什么样的。
优化前后比较
通过递归调用 requestFunction 来处理任务这种方式虽然有效但有以下几个缺点
1. 递归深度问题对于大量任务递归调用可能导致调用栈过深出现栈溢出问题。
2. 复杂性和可维护性递归调用使代码较为复杂逻辑不直观不容易维护。
3. 难以控制并发递归调用在控制并发请求数量上不够灵活。优化后的方式使用了 Promise.all通过批量处理的方式来提高代码的可读性和可靠性
1. 避免递归使用 Promise.all 处理每个批次的请求避免了递归调用导致的栈深度问题。
2. 简化代码优化后的代码结构更加清晰逻辑更加直观便于维护。
3. 控制并发批量处理的方式更加灵活可以方便地控制并发请求的数量。
4. 更好的错误处理单独处理每个请求的错误不会因为一个请求失败而导致整个批次停止优化代码
// 批量处理
const batchOperation (title, operationType, axiosConfig, shouldFilterList, tabs) {startLoading()const groupSize 5let successCount 0let errorCount 0let errorMessages []const requestTaskList shouldFilterList? tableData.value.filter((item, index, arr) arr.findIndex((val) val.id item.id) index): tableData.valueif (requestTaskList.length 0) {ElMessage.error(没有可操作的任务)stopLoading(tabs)return}const handleResponse () {if (successCount errorCount requestTaskList.length) {stopLoading(tabs)const message errorCount 0? 共 ${requestTaskList.length} 个任务全部处理成功。: 共 ${requestTaskList.length} 个任务${successCount} 个处理成功${errorCount} 个处理失败。ElNotification({title: ${title}结果,message,type: errorCount 0 ? success : warning})if (errorCount 0) {console.error(处理失败的任务, errorMessages)}}}const makeRequest (row) {let params {}return axios.request({ url: axiosConfig.url, method: axiosConfig.method, params }).then((res) {if (res.data.code 200) {successCount} else {errorCounterrorMessages.push(res.data.message)}}).catch((error) {errorCounterrorMessages.push(error.message || error)})}const executeBatch (batch) {return Promise.all(batch.map(makeRequest)).catch(() {}) // 忽略批次中的错误单独处理}let nowIndex 0const batches []while (nowIndex requestTaskList.length) {const batch requestTaskList.slice(nowIndex, nowIndex groupSize)batches.push(executeBatch(batch))nowIndex groupSize}Promise.all(batches).finally(handleResponse)
}五、总结
两者其实都是并发处理一组又一组的请求从性能上我选择 20 个任务实现的效果优化后和优化前
可以看得出其实两者没有什么区别可能因为我的实验数量太少但是我查阅了资料得出使用 Promise.all 的优点
代码简洁Promise.all 的实现方式较为简单代码可读性强。 无栈深度问题Promise.all 没有递归调用的问题不会导致栈溢出。 就以上两点也算是有了一点点效果吧。
最后想试试另一种工具库来实现这个功能就是借助上面提到的库未完~
欢迎查看: 下篇