Promises fail in unexpected fashion
See original GitHub issueGeneral information
- SDK/Library version: 2.8.0
- Environment: Both
- Language, language version, and OS: Node 8.9
Issue description
When calling methods that return a promise, they currently return successfully even if the call failed. I would think results
should never contain an error.
This is the code I would like to write:
try {
const results = await braintree.clientToken.generate({ customerId: 'invalid' });
doThingsWithValidResults(results);
} catch (err) {
handleError(err);
}
The example provided in the README defeats the purpose of Promises, as we now have to handle failure in two places:
promise().then((results) => {
// anti-pattern
if (result.success) {
console.log('Transaction ID: ' + result.transaction.id);
} else {
handleFailureHere(results.message);
}
}).catch((err) => {
handleFailureHereToo(err);
});
Maybe this is a consequence of converting this library to support promises after initially being written in the err, response
callback fashion. Would just like to see what y’all think about this.
Issue Analytics
- State:
- Created 6 years ago
- Comments:5 (3 by maintainers)
Top Results From Across the Web
Balenciaga's child ad scandal to Kanye West's antisemitic ...
Balenciaga's child ad scandal to Kanye West's antisemitic meltdown: The 7 biggest fashion industry fails in 2022. BYSophie Mellor.
Read more >State of Fashion | McKinsey
After experiencing 18 months of robust growth (early 2021 through mid-2022), the fashion industry is again facing a challenging climate.
Read more >What Is A Brand Promise? 12 Powerful Examples - Gary Fox
A brand promise is a short simple statement that tells a customer what they can expect from your brand.
Read more >The Myth of Sustainable Fashion - Harvard Business Review
Statements from fast fashion brands such as Primark (a retailer of $3.50 T shirts) that promise to “make more sustainable fashion affordable for ......
Read more >JavaScript Promises - reject vs. throw - Stack Overflow
There is no advantage of using one vs the other, but, there is a specific case where throw won't work. However, those cases...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Braintree’s underlying API that the different server SDKs use follows a result object pattern.
So, if you have a transaction that succeeds, you get:
Now, lets say you have a transaction that is processor declined:
In this case, even though the transaction failed, a transaction object is still created, so it makes sense that the promise resolves, but reports that the sale was not a success.
There are only a few cases where the promise will actually reject. One is if the Braintree Gateway cannot be reached (no internet, revoked/incorrect API keys, rate limiting):
Or if the resource could not be found:
In basically every other case, the promise will resolve with
result.success
being true or false.This pattern works very well for transactions, where you need a record of the attempted transaction even if the transaction did not actually charge the customer. It works less well for the example you gave:
Where there’s no underlying object being created.
I think we used this pattern largely because the other Braintree server SDKs used this pattern. And the reason the other SDKs used this pattern was because the alternative was to have the SDK raise an exception in the case where a transaction failed (because those languages don’t have the same async patterns as node, so you literally have to throw an error if the request failed).
I agree that this pattern is not perfect, but right now we can’t change it without both a major version bump and having a big inconsistency with the other server sdks.
We’re now working on the next major version of the Node SDK, so we’re revisiting issues like this one.
We’ve decided to not provide support for this pattern. This doesn’t throw because the transaction object is created, there was no error in the underlying API request, it’s just that the status of the transaction was not a success and should be handled appropriately.
Even if we moved the logic into the error handler, all it does is move the complexity to a different location. You’d still need to parse the error and determine if it failed because the underlying API request failed, or if the transaction object was created, but it was declined. Beyond that, we’d need to attach the transaction (and/or any other resource object) to the error, and we think it’s a cleaner implementation to retain the pattern we have.
On y’all’s end, you could push the logic around
res.success === false
into the catch block this way: