1

Looking in the NodeJS bcrypt package (https://www.npmjs.com/package/bcrypt), there appears to be pairs of functions for async/sync:

  • genSalt/genSaltSync
  • hash/hashSync
  • compare/compareSync

I understand the purpose of async functions in cases where the function has I/O such as disk or network access, so as not to block the event loop. But what about cases like the above, where there's no I/O, what advantage is there to using the async versions?

What do you lose by choosing the sync versions? I'd like to do so because it makes for simpler code and I can't see any downside to this.

In https://stackoverflow.com/a/11606391/779159 it says "you'd want to use the async version if possible so you're not tying up your node processing during the password hash", but wouldn't your code be tied up anyway since it's CPU that's being used by these functions rather than I/O?

Community
  • 1
  • 1
user779159
  • 9,034
  • 14
  • 59
  • 89
  • Other than the obvious "you have to wait" there probably isn't one in this case. – Dave Newton Nov 01 '15 at 19:54
  • @DaveNewton, wouldn't you have to wait even if the code was async? It's not like NodeJS is going to hand the operation to another thread to do the CPU work required by these functions. It's going to block the event loop in either case right? – user779159 Nov 01 '15 at 20:00
  • Depends how it's implemented. – Dave Newton Nov 01 '15 at 21:20

2 Answers2

3

If you use the async version, other code will still be let to run. For example:

Async

var startTime = new Date;

setInterval(function() {
    console.log('interval ' + (new Date - startTime));
}, 100);


setTimeout(function() {
    console.log('starting hashing');
    bcrypt.hash('bacon', 12, function (done) {
        console.log('hashing done');
    });
}, 300);

Would print:

interval 107
interval 214
starting hashing
interval 315
interval 415
interval 515
hashing done
interval 615

Sync

The sync version of the code would look like this:

var startTime = new Date;

setInterval(function() {
    console.log('interval ' + (new Date - startTime));
}, 100);


setTimeout(function() {
    console.log('starting hashing');
    bcrypt.hashSync('bacon', 12);
    console.log('hashing done');
}, 300);

And output something like

interval 105
interval 212
starting hashing
hashing done
interval 535
interval 635

I am not sure how it is done internally in the bcrypt module, maybe it's spinning up a new thread since it's native code? I guess you could have a look into bcrypts source for the details.

Henrik Karlsson
  • 5,559
  • 4
  • 25
  • 42
  • Very interesting! Could you please add equivalent code and output for how it would look if using the sync versions of the functions, for comparison purposes? At that point I think I'll understand the difference clearly and will mark this as accepted answer, thanks a lot! – user779159 Nov 01 '15 at 20:17
  • Sure, I also added some timings to the interval logs to make more sense of what's happening. – Henrik Karlsson Nov 01 '15 at 20:25
  • what you wrote proves nothing. the code will run anyway since bcrypt doesn't block. – David Haim Nov 01 '15 at 20:47
  • Yea, that's the difference I was trying to show. The argument in the question was that bcrypt would block either way since it wasn't IO-bound, which it doesn't. If bcrypt was written in pure javascript it would have to use setImmediate to do the loops in order to produce same output as my first example. (IE it's not obvious that it works like it does) – Henrik Karlsson Nov 01 '15 at 21:03
0

I've looked carefully into the native code of bcrypt I didn't see anything that may block. all the actions there are basically CPU-bound.

I think it boils down to priority. bcrypt consists out of heavy computational functions. by spinning new executor it won't be any faster (and maybe even slower), but at least other actions can be processed while the computations are being down.

David Haim
  • 25,446
  • 3
  • 44
  • 78