并发? 处理并发
因为js是单线程的,所以前端的并发指的是在极短时间内发送多个数据请求,比如说循环中发送 ajax , 轮询定时器中发送 ajax 请求. 然后还没有使用队列, 同时发送 的.
1. Promise.all
可以采用Promise.all
处理并发, 当所有promise
全部成功时, 会走.then
,并且可以拿到所有promise
中传进resolve
中的值
Promise.all([ WsApi.querySpyTaskSummary(), WsApi.querySpyTask(), ]).then((res) => { console.timeEnd(); });
注意: forEach 不能 await等待, map则可以, 结合promise完善代码
this.upTrackCard(); // 先更新一次 card
this.upFenceCard(); // 先更新一次 card
// this.onDialogOpened(); // 测试--------- 更新
// 定时器 timer 围栏
if (this.timer) clearInterval(this.timer);
this.timer = setInterval(() => {
setTimeout(() => {
this.upFenceCard(id); // 定时更新 card
}, 0);
}, 2000);
// 定时器 timer 轨迹
if (this.timerTrack) clearInterval(this.timerTrack);
this.timerTrack = setInterval(() => {
setTimeout(() => {
this.upTrackCard(id); // 定时更新 轨迹
}, 0);
}, 2000);
// 更新轨迹
async upTrackCard(id)
// 根据 mapId 查询地图上工具 tagIds
let tagIds = [];
await getToolsMapMapTags(id).then((response) => {
// console.log("地图工具信息:", response);
// 没有工具时候
if (response.length === 0) {
//
console.log("没有===========工具===========");
// this.openDetail = true; //打开弹框: 队列执行完了再打开弹框,要不然提前打开,代码却没有执行完, 造成空渲染
// this.fullscreen = true; //弹框全屏
// this.onDialogOpened(); // 测试--------- 更新
}
// 有工具的时候
else if (response.length != 0) {
//
tagIds = response.map((item) => item.id);
}
});
this.imageList = []; // 响应了再清空
let tagList = {}; // 报警
let tagIdsAll = tagIds.map(async (tagId) => {
// 根据 tagId 获取工具 当前位置 图片地址
await getToolsTracePosition(tagId).then((response) => {
// console.log(response.remark, "remark=====================");
// console.log("地图工具图片定位:", response);
let imageObject = {
tagLogoDir: response.tagLogoDir, // 工具图片地址
tagMeterX: response.tagMeterX * this.circle_PxToM_X, // x
tagMeterY: response.tagMeterY * this.circle_PxToM_Y, // y
};
this.imageList.push(imageObject);
});
try {
await Promise.all(tagIdsAll);
} catch (error) {
console.log("Promise执行中抛出了错误, 也打开弹框");
// this.openDetail = true;
}
// console.log(
// tagIdsAll,
// "Promise完成, 打开弹框---------------==============="
// );
await this.onDialogOpened(); // 测试--------- 更新
//
},
2. async/await (个人喜欢用这个)
data() {
return {
timer: null, // 定时器名称 队列
timerRefresh: null, // 定时器 2小时刷新页面
}
},
mounted() {
this.startTimer() // 定时发送请求获取数据并更新对象 3s
this.startTimerRefresh() // 定时刷新页面 2h
// this.startDayCap()// 日产能 3s
// this.startMonthCap() // 月产能 5s
// this.startOnlineTime()// 在线时长(小时) 10s
},
beforeDestroy() {
// console.log('关闭定时器')
if (this.timer) {
clearInterval(this.timer)
clearInterval(this.timerRefresh)
// clearInterval(this.timerDayCap)
// clearInterval(this.timerMonthCap)
// clearInterval(this.timerOnlineTime)
}
},
methods: {
// #####################################################################
// 定时器 队列
startTimer() {
this.fetchAll() // 开始请求一次
if (this.timer) clearInterval(this.timer) // 清空上一个定时器
// 开启定时器
this.timer = setInterval(() => {
this.fetchAll() // 机器人状态汇总
// 优化释放异步资源方案未使用
// setTimeout(() => {
// this.fetchAll() // 机器人状态汇总
// }, 0)
}, 3000)
},
//定时刷新页面
startTimerRefresh() {
if (this.timerRefresh) clearInterval(this.timerRefresh)
this.timerRefresh = setInterval(() => {
window.location.reload(true)
// 刷新
console.log("刷新");
}, 2 * 60 * 60 * 1000) // 2 h
},
//
//
//
async fetchAll() {
// 日产能定时器
await WeldHomeGetGroupDayCap().then(res => {
// console.log(res, '日--------------');
if (res.code === 200) {
//
// this.props_productComponent_day = {}
//
this.props_productComponent_day = {
dataName: res.data.map(item => item.robotName),
dataNum: res.data.map(item => item.realCap.toFixed(2) * 1)
}
// console.log(this.props_productComponent_day);
} else {
// this.msgError('err')
}
}).catch(err => {
})
// 月产能
await WeldHomeGetGroupMonthCap().then(res => {
// console.log(res, '月产能--------------');
if (res.code === 200) {
//
// this.props_productComponent_month = {}
//
// const seriesData = day_xAxis_series_Data.map((item, index) => {
// return item.map(item => {
// return Number(item.rate)
// })
// })
this.props_productComponent_month = {
robotNameList: res.data.map(item => item.robotName), // x轴
seriesData: res.data.map(item => item.realCap.toFixed(2) * 1) // y轴
}
} else {
// this.msgError('err')
}
}).catch(err => {
})
// 放 try catch也可以的,因为有的会结合使用
try {
// let 变量1
// let 变量2
// await 1
// await 2
} catch (error) {
// console.log(111);
}
}
每隔几秒请求一次接口(轮询)页面过段时间会卡死?
如果要求不高的话,最简单的就是 定时刷新, 如上边的2小时刷新方案.
当然,首先我们要排查是哪方面的错误, 后端接口的问题,还是前端代码执行顺序的问题,并发是否串行了. 等等......
eg: 某个页面放置一段时间(几分钟,几小时,几天),点不了,刷新页面也要很长时间才能响应或者不响应. 卡顿问题,只有关闭页面,重新打开才正常 ===>>> 浏览器内存堆满问题, 比较明显的,谷歌快照能看到 (performance快照、memory快照)
单纯使用setInterval会使页面卡死,setTimeout自带清除缓存,组合使用实现轮询可解决浏览器崩溃
这个setTimeout闭包也可以: 但是需要注意: demo
函数的执行时间超过定时器间隔,就会出现堆积的情况,导致性能下降甚至页面卡顿。滑动验证页面https://segmentfault.com/a/1190000038886919
window.setInterval(() => {
setTimeout(fun, 0)
}, 30000
<script>
export default {
data() {
return {
num: 0,
timer: null,
};
},
destroyed() {
//离开页面是销毁
clearInterval(this.timer);
this.timer = null;
},
created() {
// 实现轮询
this.timer = window.setInterval(() => {
setTimeout(this.getProjectList(), 0); // 发送请求
}, 3000);
},
methods: {
stop() {
clearInterval(this.timer);
this.timer = null;
},
// 请求是否有新消息
getProjectList() {
console.log("请求" + this.num++ + "次");
if(this.num==8){
this.stop()
}
}
}
};
</script>
补充说明一下: 页面卡死也可能是dom堆积没有释放造成的!!!
我最近做了个项目,konvajs绘制围栏,轨迹,工具的, 也是放在定时器中刷新的, 一般200个请求后就会内存爆了
谷歌快照可以看: 我看不懂, 就代码一步步排查了
排查1, 先后端整合接口, 单发请求,不做其他操作, 循环了2000次后,依旧不卡页面: 接口无问题
排查2, 执行js, 循环2000次后依旧咩问题
排查3, 加上konvajs绘制, 200次左右,页面卡死
结论: dom 的绘制一直是刷新的,看似替换,实则缓存了.
释放缓存, konvajs实例释放
代码优化: 只加载一次的, 只调用一次即可, 其他循环更新的,则加入到循环体中发请求更新
相同元素则做一次性处理: 比如批量圆, 图标
也可以缓存池, 已经有的只要没变则不动
eg: 详情如下,弹框中的 konvajs