Varun Agarwal Varun Agarwal - 5 days ago 4
Javascript Question

Promise.method() function not working the way I expect it to work

I am writing some code that encrypts a communication channel between two users.
The steps are as follows


  • Check if a cipher for the channel exists in
    mongoDb

  • If yes, fetch the
    cipher
    and encrypt the incoming data

  • If no, create a new cipher, save it to
    mongoDb
    and encrypt the incoming data



The code uses
crypto
module which is one of the few synchronous libs in core nodejs

Create a cipher and return it as a promise

cipher.createCipher = Promise.method((pw) => {
if (!pw) {
throw new Error('Passphrase must be provided');
}
return crypto.createCipher('aes192', pw);
});


Encrypt data using Promise.method()

cipher.encryptTextAsPromise = Promise.method((cipher, plainText) => {
if (!( typeof plainText === 'string')) {
throw new Error("2nd param must be plain text");
let cipherText = '';
cipher.on('readable', () => {
var data = cipher.read();
if (data)
cipherText += data.toString('hex');
});
cipher.on('end', () => {
return cipherText;
});
cipher.write(plainText);
cipher.end();
}
});


Encrypt data via callback functions.

cipher.encryptText = (cipher, plainText, callback) => {
if (!( typeof plainText === 'string')) {
throw new Error("2nd param must be plain text");
}
try {
let cipherText = '';
cipher.on('readable', () => {
var data = cipher.read();
if (data)
cipherText += data.toString('hex');
});
cipher.on('end', () => {
callback(null, cipherText);
});
cipher.write(plainText);
cipher.end();
} catch (e) {
callback(e, null);
}
}


I am unable to chain these two together. What I have is a horrible anti-pattern that is worse than getting stuck in callback hell

cipher.createCipher('secretWord')
.then((data) => {
cipher.encryptTextasPromise(data, 'hello world')
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
})
})
.catch((err) => {
console.log(err);
})

mongoDbSearch(username)
.then((data) => {
if (data) {
// Cipher exists, retrieve and encrypt
}else {
// Create new cipher and save
someMongoSave()
.then((data) => {
// Cipher exists now, retrieve and encrypt
})
}
})
.catch((mongoErr) => {
console.log(mongoErr);
})


The code is a bit incomplete as I am still trying to grasp this concept. Furthermore the my attempt to chain
createCipher
and
encryptTextAsPromise
is returning
undefined
in the data. I have tried writing these as normal callback and then using
Promise.promisfyAll()
as well, which just feels like another anti-pattern.

Answer

Promise.method would appear to make sense for createCipher, but probably not for encryptTextAsPromise.

Here's a version that should point you the right direction, distilling various of T.J. Crowder's, Jaromanda X's, and undefined's comments on the question; see code comments for more:

// For this one, `Promise.method` still makes sense (although doing it with
// your own promise is also perfectly reasonable)
cipher.createCipher = Promise.method(pw => {
  if (!pw) {
    throw new Error('Passphrase must be provided');
  }
  return crypto.createCipher('aes192', pw);
});

// For this one, your own promise makes sense
cipher.encryptTextAsPromise = (cipher, plainText) => {
  return new Promise(resolve => {
    if (!( typeof plainText === 'string')) {
      // It's fine to throw here (it will get converted into a rejection),
      // or add `reject` to the arguments list above and call
      // that and return instead:
      // reject(new Error("2nd param must be plain text"));
      // return;
      throw new Error("2nd param must be plain text");
    }
    let cipherText = '';
    cipher.on('readable', () => {
      var data = cipher.read();
      if (data)
        cipherText += data.toString('hex');
    });
    cipher.on('end', () => {
      resolve(cipherText);
    });
    cipher.write(plainText);
    cipher.end();
  });
};

// Usage:
cipher.createCipher('secretWord')
.then(data => cipher.encryptTextAsPromise(data, 'hello world'))
.then(data => console.log(data)) // See 1 below
.catch(err => console.log(err));

mongoDbSearch(username)
.then(data => data || someMongoSave(data)) // You pass `data` to `someMongoSave`, presumably?
.then(data => {
    // Cipher exists, retrieve and encrypt
})
.catch(mongoErr => {
    console.log(mongoErr);
});

Separately, re

if (!( typeof plainText === 'string')) {

there's a !== operator in JavaScript. Jus' sayin'. ;-D

Comments