Both ES6 generators and node-fibers can be used to block a coroutine while waiting on some I/O without blocking the entire process. Both can do this for arbitrarily deep call stacks. The main difference between the capabilities of the two is how explicit the syntax is.
In code that uses ES6 generators:
var run = require('gen-run'); // https://github.com/creationix/gen-run