我需要在套接字服务器上的节点中进行一些CPU繁重的计算,但我不想阻塞我的IO。
是否可以使用 ES6/ES7 功能以良好的方式实现这一目标。
我的第一次尝试是使用 async 和 await,但似乎,如果在计时器事件之前调用繁重的同步函数,则计时器事件仅在执行所有繁重的计算调用后才会被调用。https://jsfiddle.net/bz31vk97/1/
let t = 0
setInterval(async function() {
t = t + 1
console.log("timer: ", t)
}, 1000)
async function syncDelay(ms) {
console.log("syncDelay: ", ms, "ms")
let t1 = Date.now()
while (Date.now() - t1 < ms) {}
return ms
}
async function heavyWork() {
for (let i = 0; i < 10; i++) {
await syncDelay(500)
}
}
heavyWork()
console.log("after calling heavyWork")
当我在节点上用 setImmediate 替换等待调用时相同:
setImmediate(() => syncDelay(500))
两个版本都首先调用所有 syncDelays,然后才开始调用计时器事件。
有没有办法允许在几个 CPU 密集型调用之间调用 IO 和计时器事件?
我想出的解决方案之一是制作一个工作管理器,它将工作保留在队列中,并在上一个工作结束后将下一个工作添加到 javascript eventloop 中。这样,其他事件可能会在 CPU 繁重的工作负载部分之间发生。
https://jsfiddle.net/bz31vk97/2
let t = 0
setInterval(async function() {
t = t + 1
console.log("timer: ", t)
}, 1000)
async function syncDelay(ms) {
console.log("syncDelay: ", ms, "ms")
let t1 = Date.now()
while (Date.now() - t1 < ms) {}
return ms
}
class WorkManager {
addWork(work) {
if (this.first === undefined) {
this.first = work
this.last = work
this.runWork()
} else {
this.last.next = work
this.last = work
}
}
newWork(fn, ...args) {
this.addWork({fn, args})
}
runWork() {
let work = this.first
if (this.first === undefined) {
return
}
setTimeout(() => {
this.first = this.first.next
work.fn(...work.args)
this.runWork()
}, 0)
}
}
let wm = new WorkManager()
for (let i = 0; i < 10; i++) {
wm.newWork(syncDelay, 500)
}
console.log("after calling heavyWork")