2

Question: util.promisify converts an async function that uses the error-first callback style to a Promise. However it doesn't seem to work with AWS' S3.upload (scroll down to .upload) which is an async function that uses the error-first callback format.

Original format:

const params = {Bucket: 'bucket', Key: 'key', Body: stream};
s3.upload(params, function(err, result) {
    if (err) {
        throw new Error(err);
    } else {
        console.log(result);
    }
});

Promisify format:

const params = {Bucket: 'bucket', Key: 'key', Body: stream};
const s3Upload = util.promisify(s3.upload);
s3Upload(params)
    .then( (result) => {
        console.log(result);
    })
    .catch( (err) => {
        throw new Error(err);
    });

However used in this manner s3.upload throws an error,

TypeError: service.getSignatureVersion is not a function

If util.promisify doesn't necessarily work for every async function that uses a callback is there a way to determine beforehand if a function will work with it? Or alternately does it take trial and error to determine if util.promisify will work?

myNewAccount
  • 578
  • 5
  • 17
  • 2
    It takes trial and error to determine if `util.promisify` will work, use `await s3.upload(params).promise()` instead – Lawrence Cherone Mar 23 '21 at 01:03
  • @LawrenceCherone Awesome! Where did that `.promise()` come from? I didn't know that method was available. Where can I read about it? – myNewAccount Mar 23 '21 at 01:06
  • 1
    @LawrenceCherone I found it! https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html – myNewAccount Mar 23 '21 at 01:08
  • 1
    `util.promsify()` works in the simple manner you are using it ONLY when the method you're calling is stand-alone and does not require that it is called with some sort of instance. If it must be called with an instance, then you have to bind that instance to it as Geoffrey shows you in his answer. – jfriend00 Mar 23 '21 at 01:14

2 Answers2

3

Before answering your question, I'd like to point out that callback style functions are usually not async functions. util.promisify wraps a callback style function with a promise, which is the same thing as wrapping the callback style function in an async function.

To fix your issue you may need to properly set the this context for the upload funciton manually. That would look something like this:

const s3Upload = util.promisify(s3.upload.bind(s3));

Otherwise, the issue you are having is either a Node.js bug or a AWS bug, and I would reccommend filing a bug report.

  • I'm going to use jfriend00's `.promise()` method to solve this problem since it is built in. However I tested your `.bind(s3)` method and it worked without error. That blows me away. How did you know that would work? To me it's not intuitive. Where could I read about that? – myNewAccount Mar 23 '21 at 01:36
  • 1
    [Here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions) is a good general overview about JS functions. [This](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) is the Function class reference. I reccomend skimming over the first link and then check out the documentation for the Function class's instance methods. – Geoffrey Casper Mar 23 '21 at 01:42
  • 1
    As a general note, just search "js [topic/js `class`] reference" and 9 times out of ten it should bring you to the appropriate Mozilla documentation for the topic or JS `class` your are looking into. – Geoffrey Casper Mar 23 '21 at 01:44
3

Geoffrey has explained why this probably doesn't work for the code you tried, but even better would be to use the promise support built-into the AWS interface:

const params = {Bucket: 'bucket', Key: 'key', Body: stream};
s3.upload(params).promise().then((result) => {
    console.log(result);
}).catch( (err) => {
    console.log(err);
    throw err;
});

AWS documentation here: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html

jfriend00
  • 683,504
  • 96
  • 985
  • 979