2

This tiny Javascript program returns the sha256 hash of its text.

const shasum = require('crypto').createHash('sha256');
const stream = require('fs').createReadStream(__filename);

stream.on('error', function() { console.log('Error.'); });
stream.on('data', function (chunk) { shasum.update(chunk); }); /* <--- data line */
stream.on('end', function() {
    const sha = shasum.digest('base64');
    console.log(`The sha is ${sha}`);
});

Executed with Nodejs Erbium, it works as expected.

However, after writing it I thought that the function expression was not needed and so I changed the data line with the following:

stream.on('data', shasum.update);

And it crashes with a horrific error message:

  if (state[kFinalized])
           ^

TypeError: Cannot read property 'Symbol(kFinalized)' of undefined
    at ReadStream.update (internal/crypto/hash.js:78:12)
    at ReadStream.emit (events.js:311:20)
    at addChunk (_stream_readable.js:294:12)
    at readableAddChunk (_stream_readable.js:275:11)
    at ReadStream.Readable.push (_stream_readable.js:209:10)
    at internal/fs/streams.js:210:12
    at FSReqCallback.wrapper [as oncomplete] (fs.js:487:5)

Javascript is very flexible with function calls, but according to the documentation the stream.on data call should pass only one parameter.

Why is the behavior different?

Paolo.Bolzoni
  • 2,416
  • 1
  • 18
  • 29
  • 1
    Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – VLAZ Aug 06 '20 at 06:36
  • 1
    Basically if you just want to use a function reference, you need `shasum.update.bind(shasum)`. However, I personally don't like it - it looks odd to have a "sandwich" of the context. If you want it shorter, just use an arrow function `chunk => shasum.update(chunk)` – VLAZ Aug 06 '20 at 06:38

1 Answers1

1

The issue is the context.

The stream will bind the data function to the stream itself

stream.on('data', function (chunk) {
  console.log(this) // it is the `stream`
  shasum.update(chunk)
})

In this case, the shasum.update is bind to the stream, so the update function will not work:

function update(data, encoding) {
  const state = this[kState];
  if (state[kFinalized])
    throw new ERR_CRYPTO_HASH_FINALIZED();

To let it works you must write this statement:

stream.on('data', shasum.update.bind(shasum))
Manuel Spigolon
  • 11,003
  • 5
  • 50
  • 73