前端异步加载多个分包数据的处理以及单线程与多线程对比
使用场景
前端需要加载一些分包的数据,最终拼成一个完整数据包时,需要考虑回包的顺序以及最后一个回包收到的事件通知。最容易想到的方式是先分配好每个数据包的位置,每次收到数据包时,检查数据包缓存是否排满,如果排满就使用发布订阅模式通知事件。
最佳实现方式
直接切入正题,走过了几条弯路以后才发现Promise.all方法才是最合适的解决方式。
//单线程
async function mainLoad() {
let arr = modelObj.bagInfo.body.map((v) => {
//这里可以是任何promise封装过的异步函数
return ajaxGet( v ,"arraybuffer")
})
modelObj.vertex = await Promise.all(arr)
}
没错,就是这么简单,Promise.all会根据arr参数里promise对象的顺序去返回相同顺序的数据包。有类似需求的同学们,请认准Promise.all。
单线程加载与多线程加载的对比
如果使用多线程分包加载数据会不会快呢?
h5标准的worker线程也出来很多年了,如果只是纯前端开发,基本用不到。写个东西试试,用数据说话。
//多线程
async function workerLoad() {
let arr = modelObj.bagInfo.body.map((v,i) => {
return spawnWorker(i)
})
modelObj.vertex = await Promise.all(arr)
}
// 生成多线程
function spawnWorker(i) {
return new Promise((resolve, reject) => {
let worker = new Worker('./worker.js', { name: i })
worker.postMessage(modelObj.bagInfo.body[i])
worker.onmessage = function (event) {
resolve(event.data.res)
}
})
}
worker.js里的代码就是个ajax请求分包数据,收到后通知主线程,注意worker线程有一些内置对象是获取不到的,所以它能干的事情有限。
分5个数据包加载,分大包和小包,下面是各类情况下的结果。
多线程加载大包或小包都要更慢???
看看下面的waterfall图就知道了,多线程还要多调用5个worker去生成ajax请求,线程之间还要有开销,所以肯定就慢了。
观察每一个请求发送的时间,单线程因为异步的关系,与多线程一样,几乎都是同时发送的。
查询有关浏览器事件循环模型相关资料,ajax请求本来就是启用其他线程去完成的,所以用worker纯属多此一举。
难道worker线程在前端就是鸡肋么?
当然不是,在webGL领域以及前端人工智能领域,把一些大计算量的工作放在worker去处理才能充分利用多核cpu,做做页面的话好像确实用不到。