Node's crypto module began development before node's architecture was finalized.
What is now written as
crypto.createHmac('sha1').update(bytes).digest('hex');
will someday be written more like
var cryptoStream = crypto.create('hmac', 'sha1');
byteStream.pipe(cryptoStream);
cryptoStream.on('end', function (digest) {
console.log(digest.toString('hex'))
});
Otherwise, the update function becomes blocking.
Although node will probably always be backwards compatible, I was porting this to the browser - which is just a few lines of changes thanks to your clean and excellent code - and the WebCrypto API is already asynchronous (Promise, not callback) so the port is not 1:1.