Worker中Close和Terminate的区别
需求背景
本来有个小需求,就是根据数据来绘制对应Canvas再转为base64直接当做background-image来当背景图用,但绘制频率可能会很高,怕在主线程中发生阻塞用户操作的情况,所以考虑将绘制需求挪至Woker中来进行离屏Canvas的绘制。
不过有一种场景是用户操作时,Worker中的Canvas尚未完成绘制,用户就取消了操作,相应的Canvas的绘制也应该同时被撤销,而Worker又是独立线程只能等待执行完成后再响应下一次事件。 再查看了Woker的API后,找到了Terminate和Close两个方法。
官方定义
terminate:Worker 接口中的 terminate() 方法用于立即终止 Worker 的行为. 本方法并不会等待 worker 去完成它剩余的操作,worker 将会被立刻停止。
close:用于Woker内部关闭自身。
思考一下
Woker虽然是独立线程,但也有自身的Event Loop,这时两者关闭的时机就产生了区别,脑补一下,close是内部关闭自身,那一定是队列中排在关闭前面的事件全部执行完后才能关闭,而terminate则是主线程中的事件,我对于官方所谓的立即关闭机制保持怀疑态度,所以两者关闭的时机就是本次测试的重点。
测试
- 实验1:terminate()
// 主线程
this.canvasInWorker.onmessage = (e) => {
this.canvasInWorker.terminate()
}
// Worder
console.time('runtime')
for (var i = 0; i < 10000 * 10000 ; i++) {
if (i === 1) postMessage({imageBitmap: imageBitmap}, [imageBitmap])
if (i > (10000 * 10000) - 10) console.log('i', i)
}
console.timeEnd('runtime')
输出结果:
思考:terminate并没有立即停止,怀疑是主线程的terminate去关闭worker所执行的时间超过了worker内部循环1亿次的239.56ms,所以要再次进行一次实验增加循环次数至10亿次。
- 实验2:terminate()
// 主线程
this.canvasInWorker.onmessage = (e) => {
this.canvasInWorker.terminate()
}
// Worder
console.time('runtime')
for (var i = 0; i < 10000 * 10000 * 10 ; i++) {
if (i === 1) postMessage({imageBitmap: imageBitmap}, [imageBitmap])
if (i > (10000 * 10000 * 10) - 10) console.log('i', i)
}
console.timeEnd('runtime')
输出结果:
思考:当然循环增加至10亿次时,打印没有出现,代表在循环执行完之前,terminate已经将Worker关闭了。
- 实验3: close()
// Worder
console.time('runtime')
for (var i = 0; i < 10000 * 10000 * 10 ; i++) {
if (i === 1) self.close()
if (i > (10000 * 10000 * 10) - 10) console.log('i', i)
}
console.timeEnd('runtime')
输出结果:
思考:果然同样是10亿次的大循环,close却是在循环结束之后才关闭了workder的,这也证明了之前的猜想。
- 实验4:主线程通过事件触发close()
// 主线程
this.canvasInWorker.onmessage = (e) => {
this.canvasInWorker.postMessage({ cmd: 'stop' })
console.log('close')
}
// Worder
console.time('runtime')
for (var i = 0; i < 10000 * 10000 * 10 ; i++) {
if (i === 1) postMessage({imageBitmap: imageBitmap}, [imageBitmap])
if (i > (10000 * 10000 * 10) - 10) console.log('i', i)
}
console.timeEnd('runtime')
输出结果:
思考:与自身调用close一样,即使是主线程通知self,依旧要等待自身事件机制的循环走完,才会执行对位的self.close。