Aws-sdk-js: bluebird with aws-sdk: Cannot promisify an API that has normal methods with 'Async'-suffix

Created on 30 Aug 2018  Â·  14Comments  Â·  Source: aws/aws-sdk-js

Seems there is a bug when working with [email protected] and [email protected]
const bluebird = require('bluebird');
const AWS = require('aws-sdk');
const AWSSQS = new AWS.SQS();
bluebird.promisifyAll(AWSSQS);

trigger an error: TypeError: Cannot promisify an API that has normal methods with 'Async'-suffix
See http://goo.gl/MqrFmX

the problem probably because you added function with 'Async' suffix.

third-party

Most helpful comment

We had same issue.. the root cause of our issue has to do with dynasty package not locking into one version ^ of aws-sdk.

Also AWS-SDK team should of never changed a callback to a promise in a minor release as this is a breaking change.

All 14 comments

We had same issue.. the root cause of our issue has to do with dynasty package not locking into one version ^ of aws-sdk.

Also AWS-SDK team should of never changed a callback to a promise in a minor release as this is a breaking change.

From http://bluebirdjs.com/docs/api/promise.promisifyall.html:

If a method name already has an "Async"-suffix, it will be duplicated. E.g. getAsync's promisified name is getAsyncAsync.

Apparently Bluebird changed this behaviour, and throws an exception now.

The same happened to us. Could that be the reason for it https://github.com/aws/aws-sdk-js/commit/fe88308a8699b39aef06492c898b265a3a24251f#diff-cca1065697cbb567e3320496b1f7448fR43? That's hilarious.

@DanielSuperSonic
It shouldn't be necessary to promisify the SDK. If promises are available in your environment, any operation can return a promise by calling the promise() method on the operation's return value:

// with callbacks
sqs.listQueues(function(err, data) {});

// using .promise
sqs.listQueues().promise();

If you specifically want to use the bluebird implementation of promises, you can configure the SDK to use that instead:

AWS.config.setPromiseDependency(require('bluebird'));

@jasonfutch
You can still use callbacks with the SDK. To avoid making a breaking change, we only return promises on operations if you explicitly call the promise() method on the operation's return value.

~Though, it looks like the promise() method doesn't exist on .upload, per this comment?~

Never mind, s3.upload(params).promise() worked exactly as I'd hope!

@chrisradek we happen to have some legacy code that we don't want to update just yet. I haven't been working with the SDK for some time but I thought that a promise() property was the pattern to return a promise, not an Async suffixed function.

Anyway, this incident was so random Boone could have saw it coming. :)

@chrisradek regarding https://github.com/aws/aws-sdk-js/issues/2229#issuecomment-417386675 :

It shouldn't be necessary to promisify the SDK. If promises are available in your environment, any operation can return a promise by calling the promise() method on the operation's return value:

// with callbacks
sqs.listQueues(function(err, data) {});

// using .promise
sqs.listQueues().promise();

I initially liked that path, but after moving a chunk of existing code to .promise(), I'm going to stay with Bluebird,

  • Either using a custom suffix other than Async...
  • ... or replacing promisifyAll with specific promisify calls (thus avoiding collisions when/if AWS introduces new foo/fooAsync APIs like done in https://github.com/aws/aws-sdk-js/commit/fe88308a8699b39aef06492c898b265a3a24251f with on/onAsync).

Because all things considered, promisifying callback-based APIs is a pattern I'm okay with, and AWS promise() sounds neat, but makes mocking a bit more painful and a lot uglier:

// stubbing a promisified DynamoDB.DocumentClient
sinon.stub(dynamo.documentClient, 'putAsync')
     .resolves({ Attributes: response });

// stubbing a DynamoDB.DocumentClient using promise()
sinon.stub(dynamo.documentClient, 'put')
     .returns({ promise: () => (new Promise(resolve => (resolve({ Attributes: response })))) });

@jasonfutch when did that change occur, do you know?

It happened Wednesday about 4pm EST, it was with release 3.305.0

On Aug 31, 2018, at 7:40 PM, Mike Flores notifications@github.com wrote:

@jasonfutch when did that change occur, do you know?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

Thanks @jasonfutch. Moving back and pegging aws-sdk-js to version 2.304.0 is the perfect workaround for now.

What's wrong with using the latest version of the sdk and a different suffix?

bluebird.promisifyAll(AWSSQS, {suffix: 'Promise'})

What's wrong with using the latest version of the sdk and a different suffix?

This would require existing code (using ...Async until now) to be modified, which clearly should not be the intent of a patch semver release.

We can change suffix while doing promisifyAll
Promise.promisifyAll( Object target, [Object { suffix: String="Async", multiArgs: boolean=false, filter: boolean function(String name, function func, Object target, boolean passesDefaultFilter), promisifier: function(function originalFunction, function defaultPromisifier) } options] ) -> Object
http://bluebirdjs.com/docs/api/promise.promisifyall.html

I changed to 'Async2' and it works well

A benefit of not needing to use a custom promise suffix is that when AWS finally gets their game together and releases a proper async update with native Promise support, all you have to do is drop the promisified import and replace it with a regular one and the rest of your code remains as-is, no revision history butchery involved.

Does anyone know if there's an option to disable the newer "exception on existing async method" "feature" in bluebird or else configure it to ignore routines for which there is an existing async version?

Was this page helpful?
0 / 5 - 0 ratings