One thing to consider as a second pass, would be to wrap this in a pool or queue that returns promises. capture the worker, a resolve/reject and the on handlers use the captured handlers or throw.
This way you can do expensive processors as a limited pool of workers. I've done similar to this with separate child_processes before.