caching promises leads to Unhandled promise rejection in Node.js - node.js

using nodejs v. 7.5.0 I'm getting
UnhandledPromiseRejectionWarning && DeprecationWarning
I know its part of new features since node 6.6 but the thing I don't understand is that I'm catching that promises just after caching it into a variable. If I don't cache it no warning is thrown.
this is the code that throws the error:
let verifyPromise = verifyToken(id_token);
verifyPromise.catch((err) => {
log(err);
});
let verifyOkPromise = verifyPromise.then((login) => {
return DB_API.getTokenById(id_token);;
});
verifyOkPromise.catch((err) => {
log('error in finding token: ', err);
});
verifyOkPromise.then((dbRes) => {
log('loggin res in finding token: ', dbRes);
});
where verifyToken() is a function that checks google auth token and returns a promise.
node output is the following:
error in finding token: { CouchbaseError message: 'The key does not exist on the server', code: 13 }
(node:10961) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): CouchbaseError: The key does not exist on the server
(node:10961) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
as you can see the promise error branch is regularly catched as it logs as expected but I still get the warning!!!!
while if I just append the catch like this:
verifyPromise.then((login) => {
return DB_API.getTokenById(id_token);;
}).catch((err) => {
log('error in finding token: ', err);
});
NO Warning is given!!!
I think I'm missing something subtle but I don't understand what could be.
Some one have any clues?
thanks in advance

This issue is happening because it is not the original promise that ultimately fails, it is the then()! You are not storing the result of
verifyOkPromise.then((dbRes) => {
log('loggin res in finding token: ', dbRes);
});
It is the call to then() here that ultimately triggers the resolution of the Promise chain. You're trying to handle the catch from the original call, but it's the chain that fails. Here are three simplified versions of what you're trying to do. The first matches your workflow. The second matches how these are normally written (but a pattern you didn't want to use). The third rewrites your pattern to work properly but using results assigned to variables.
let Promise = require('bluebird');
function forceReject() {
return Promise.reject('deliberately failed');
}
let p = forceReject('12345');
// This will produce an Unhandled Rejection error
/*
p.then(res => { console.log('succeeded: ', res); });
p.catch(err => { console.log('failed: ', err); });
*/
// This will work but it's not the pattern you prefer
/*
p.then(res => {
console.log('succeeded: ', res);
}).catch(err => {
console.log('failed: ', err);
});
*/
// This will also work! Note the assignment of the result of p.then()...
let q = p.then(res => { console.log('succeeded: ', res); });
q.catch(err => { console.log('failed: ', err); });

You're using Node 7.5, you should consider async/await. Chad's answer is well written and correct, but here's how I'd write the code in question:
async function whateverYoureDoing(id_token) {
const token = await verifyToken(id_token);
// catch will automatically propagate.
//You can use regular try/catch and if rethrow your own custom TokenInvalidError object
const dbRes = await DB_API.getTokenById(id_token);
console.log('loggin res in finding token: ', dbRes);
}
Note: You no longer need to add .catch and log error with promises - that's the point of the unhandledRejection tracking feature :) Logging should be generally done at a top level since promises work great with exceptions anyway.

Related

Node.js concurrent API GETs, return as soon as one responses

I have a list of APIs I want to call GET simultaneously on all of them and return as soon as one API finishes the request with a response code of 200.
I tried using a for-loop and break, but that doesn't seem to work. It would always use the first API
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
let response;
for (const api of listOfApi) {
try {
response = await axios.get(api, {
data: {
url: 'https://example.com/',
},
});
break;
} catch (error) {
console.error(`Error occurred: ${error.message}`);
}
}
You can use Promise.race() to see which of an array of promises finishes first while running all the requests in parallel in flight at the same time:
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
Promise.any(listOfApi.map(api => {
return axios.get(api, {data: {url: 'https://example.com/'}}).then(response => {
// skip any responses without a status of 200
if (response.status !== 200) {
throw new Error(`Response status ${response.status}`, {cause: response});
}
return response;
});
})).then(result => {
// first result available here
console.log(result);
}).catch(err => {
console.log(err);
});
Note, this uses Promise.any() which finds the first promise that resolves successfully (skipping promises that reject). You can also use Promise.race() if you want the first promise that resolves or rejects.
I think jfriend00's answer is good, but I want to expand on it a bit and show how it would look with async/await, because that's what you are already using.
As mentioned, you can use Promise.any (or Promise.race). Both take an array of promises as argument. Promise.any will yield the result of the first promise that resolves successfully, while Promise.race will simply wait for the first promise that finishes (regardless of whether it was fulfilled or rejected) and yield its result.
To keep your code in the style of async/await as it originally was, you can map the array using an async callback function, which will effectively return a promise. This way, you don't have to "branch off into .then territory" and can keep the code more readable and easier to expand with conditions, etc.
This way, the code can look as follows:
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
try {
const firstResponse = await Promise.any(listOfApi.map(async api => {
const response = await axios.get(api, {
data: {
url: 'https://example.com/',
},
});
if (response.status !== 200) {
throw new Error(`Response status ${response.status}`, {cause: response});
}
return response;
}));
// DO SOMETHING WITH firstResponse HERE
} catch (error) {
console.error('Error occured:', error);
}
Side note: I changed your console.error slightly. Logging only error.message is a common mistake that hinders you from effective debugging later on, because it will lack a lot of important information because it prints only the message and not the error stack, the error name or any additional properties the error may have. Using .stack and not .message will already be better as it includes name and stack then, but what's best is to supply the error as separate argument to console.error so that inspect gets called on it and it can print the whole error object, with stack and any additional properties you may be interested in. This is very valuable when you encounter an error in production that is not so easy to reproduce.

Node.js Promise without returning it

I have a use case to resolve a Promise without returning it. Catching for errors internally, but don't want the caller to wait for the promise to resolve.
doSomething()
{
Promise.resolve()
.then(() => {
// do something.
})
.catch(reason => {
this.logger.error(reason);
});
}
Getting this error:
(node:2072) Warning: a promise was created in a handler at internal/timers.js:439:21 but was not returned from it, see http://. goo.gl/rRqMUw
at Function.Promise.cast (.../node_modules/bluebird/js/release/promise.js:225:13)
Just return something from the Promise callback where you are creating the fire and forget promise.
I'm guessing that handler is doSomething
doSomething()
{
Promise.resolve()
.then(() => {
// do something.
})
.catch(reason => {
this.logger.error(reason);
});
return null //or anything else that's sensible
}
Note: We usually ignore the error message, but sometimes they contain valuable information. In your error there's a link http://. goo.gl/rRqMUw that explains exactly this problem:d

assert.throws says => Missing expected exception

I am using this chunk of code:
assert.throws(async () => {
patientSubscriber = await PatientSubscriber.create({
isSubscribed: true,
patient: patient._id,
subscriber: user._id
});
},{
message: /foo/
});
when I run it I get this:
AssertionError [ERR_ASSERTION]: Missing expected exception.
The assert API is:
https://nodejs.org/api/assert.html#assert_assert_throws_fn_error_message
Maybe I am hitting this issue?
https://github.com/nodejs/node-v0.x-archive/issues/8368
Anyone know what that error is about?
The error means that there was no exception while it was expected. async functions are syntactic sugar for promises. They never throw exceptions but may return rejected promise.
Since the assertion is expected to be synchronous, it's impossible to assert promises this way; the entire callstack should be promise-based.
As it was mentioned in comments, assert.rejects appeared in Node 10 and results in rejected promise on assertion fail.
In case promises are handled by a caller (e.g. in most testing frameworks), rejected promise needs to be returned from current function:
it('...', async () {
await assert.rejects((async () => {})()); // current function returns rejected promise
});
In case assertion promises aren't handled (e.g. in production), they result in UnhandledPromiseRejectionWarning console output, not in exceptions. unhandledRejection handler can be set to handle asynchronous errors:
process.on('unhandledRejection', err => {
console.error(err);
process.exit(1);
});
...
assert.rejects((async () => {})());

Node.js - throw exception inside ES6 promise chain

As part of an Node.js/Express API I am developing, I have built a custom error handler that lets me throw specific errors when needed e.g. BadRequest or NotFound etc.
The problem occurs when I wish to throw within a promise chain. For example:
db.doSomethingAsync().then(data => {
if(data.length === 0){
throw new errors.ResourceNotFound({ resource: "foo" });
}
});
After much reading on the topic, I see that this error will be swallowed by the promise and hence causes an unhandled Promise rejection.
I am aware I can reject however im not sure how I can then handle the specific error (rather than a catchall on reject which I dont want).
Also, to reject, would I not need to create a new promise inside of my promise chain? That feels messy.
Can anyone advise how to handle throwing a specific exception within a promise chain?
In addition to the first callback to then() which is resolve callback and recieves data, you could also provide a second callback to it which is reject callback and it recieves errors. so you could catch this specific error on the second callback of the next then:
db
.doSomethingAsync()
.then(data => {
if(data.length === 0){
throw new errors.ResourceNotFound({ resource: "foo" });
}
})
.then(massagedData => {
// work with massagedData here
}, err => {
// handle err here which is previous thrown error
// assert.ok( err instanceof errors.ResourceNotFound() )
})
.catch(err => {
// this will catch unhandled errors if any
});
then() by default returns a promise and if any of its callbacks ( whether the first or the second one ) throws an error then it returns a rejected promise and its reason will caught by next reject callback if any or by catch at the end. So you don't need to create a new promise.
See Promise.prototype.then()

Stopping the promise chain midway

I am trying to stop a promise chain midway (After a catch). So after an error occurred in the first promise the catch will catch it but I don't want the chain to continue. I'm using bluebird. How would I do this?
getRedirectedURL(url).then(function(url) {
console.log(1);
url = domainCleanse(url);
sql = mysql.format(select, url);
return [ url, mysqlQuery(sql) ];
}).catch(function(error) {
console.log(2);
console.error(error);
socket.emit('error:unreachable', url + ' was unreachable');
}).spread(function(url, rows) {
console.log(3);
if(_.isEmpty(rows[0])) {
socketList.push({
url: url,
ttl: _.now(),
socket: socket,
added: false
});
} else {
socket.emit('done', mapResults(rows[0]));
}
}).catch(function(error) {
console.log(4);
console.error(error);
socket.emit('error', 'We could not reach ' + url + ' at this time.');
});
Generalizing your example, it looks like this:
promiseToFoo()
.then(promiseToBar)
.catch(failedToFooOrBar)
.then(promiseToFrob)
.catch(failedToFrob)
Along the happy path you are promising to Foo, then to Bar, then to Frob. Based on your description, you want to handle errors Fooing or Barring separately from errors Frobbing. So a simple solution is bury the error handling for Frob into that promise. So instead of chaining a promise to Frob, you're chaining a promise to Frob and handle errors in Frobbing. Something like this:
promiseToFoo()
.then(promiseToBar)
.catch(function (error) {
failedToFooOrBar(error);
return Promise.reject(error);
})
.then(function (x) {
return promiseToFrob(x).catch(failedToFrob);
});
A key to this is to make sure that your on-reject handler in the first catch ends up leaving the chain in a rejected state when it leaves. This is handled in the sample above by returning a rejected Promise from the handler. You can also handle it by throwing an Error from the handler. If you don't do one of these things, then the promise will be in a fulfilled state when the handler finishes and the on-fulfill handler provided by the subsequent then call will be invoked.

Resources