0

I need to do some CPU heavy computation in node on socket server, but I don't want to block my IO.

Is it possible to use ES6 / ES7 features to achieve this in nice way.

My first attempt was to use async and await, but seems like, if heavy synchronous functions are called before timer events, timer events gets called only after all heavy computation calls are executed. 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")

Same when I replace await call with setImmediate on node:

setImmediate(() => syncDelay(500))

Both versions first calls all syncDelays and only then starts to call timer events.

Is there any way to allow IO and timer events to be called between several cpu heavy calls?

codez
  • 1,381
  • 1
  • 18
  • 28
  • `async`/`await` is only good for *asynchronous* heavy work. You're not even having a single promise in there that fulfills after some really asynchronous task. And all the [promise callbacks take precedence over the interval callback](https://stackoverflow.com/q/42800326/1048572). – Bergi May 25 '17 at 18:20

1 Answers1

0

One of the solutions I came up is to make a WorkManager that keeps works in queue and adds next work to javascript eventloop after previous has ended. That way other events can stick in between cpu heavy workload parts.

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")
codez
  • 1,381
  • 1
  • 18
  • 28