Node.js: Unhandled promise rejection - UnhandledPromiseRejectionWarning - javascript

I have a Node.js app. This app has a button that starts a process. The steps in that process return promises. I'm trying to chain these promises together. For some reason, I'm receiving an UnhandledPromiseRejectionWarning. However, in my mind, I've set this up correctly. My code looks like this:
var myButton = document.getElementById('myButton');
if (myButton) {
console.log('here');
myButton.addEventListener('click', executeAction('0'));
}
function executeAction(input) {
let param1 = 'A';
let promise = new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
return promise;
}
function executeStep1() {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
resolve('[something]');
}, 3000);
} catch (ex) {
reject();
}
});
return promise;
}
function executeStep2(p1, p2, p3) {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
console.log('step 2 has executed');
resolve('awesome!')
}, 3000);
} catch (ex) {
reject(ex);
}
});
return promise;
}
I've confirmed that the executeStep2 function runs to completion. I'm basing this in the fact that I can see "step 2 has executed" in the console window. However, to my surprise, I never see "All done" printed in the console window. Instead, I see the UnhandledPromiseRejectionWarning mentioned above. I don't understand two things about this result:
Why am I not seeing "All done" in the console? Shouldn't that function get executed after executeStep2 has resolved?
Where is the rejection coming from? I don't see anything that's rejecting this.
Thank you very much for your help!

executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => { // no function(result) here
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;

The error is generated from when you call the function(s) that uses promises:
myButton.addEventListener('click', executeAction('0'));
You need to catch rejections there also.
myButton.addEventListener('click', executeAction('0')
.catch((error) => console.log('ERROR', error));
The rejections are caught inside the functions, but not in the outer scope because executeAction('0') returns a promise, or it would but you are using it as a non-async function, so its creating a promise and then returning a pending promise without waiting for it to be resolved. That looks like what's causing the rejection, and its also not handled for the above reason.
This will fix it:
function executeAction(input) {
let param1 = 'A';
return new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
}
You should look into async/await. It can clean this code up significantly.
async function getSomething() {
try {
// throw 'Test error detected.'
return 'test'
}
catch (e) {
throw e
}
}
async function testing() {
try {
const sample = await getSomething()
return sample
} catch (e) {
throw e
}
}
testing()
.then((data) => console.log(data))
.catch((err) => console.log(err))
Run this above example, and then uncomment the throw. Use this pattern for maximum winning. Exceptions should be thrown to the surface level where the functions are called, displayed, used, rendered, etc. Functions should simply throw the error out.
This allows you to use error handling middleware, higher-order functions, listeners, logging, etc.

Related

ES6 Promises confusion

I have the following snippet of code inside asynchronous function -
await application.save((err) => {
console.log("hello");
if (err) {
res.status(500).send({ message: "Error encountered" });
}
});
console.log("hey");
Why does "hey" get printed out earlier than "hello"? And how to fix it so the behaviour is as expected (asynchronous save operation is waited for and only when it's done and "hello" is printed, "hey" should be printed).
Following code does actually save object to MongoDB, but when I use application.save().then(() => {}) I get an error "Cannot read property 'then' of undefined"
You are confusing between callbacks and promises.
For example, we have first task F, long task T and next task N. We want to ensure the code run in the order F -> T -> N. To do that, we need either callbacks or promises.
Callbacks
function F() {}
function T(cb) {
// do that long task here
cb(err); // then call the cb and pass is the err
}
function N() {}
function main() {
F();
cb = (err) => {
if (err) { // handle err }
else {
N() // N needs to be run in cb
}
// clean up
// N can also be put here if you want to guarantee N will run
};
T(cb);
}
Promises
function F() {}
function T(cb) {
return new Promise((resolve, reject) => {
// do that long task here
if(err) {
reject();
} else {
resolve();
}
});
}
function N() {}
// using .then .catch .final
function main() {
F();
T().then(N())
.catch(() => {
// handle error
})
.finally(() => {
// clean up
// N can also be put here if you want to guarantee N will run
})
}
// using async/await
async function main() {
F();
try {
await T();
N();
} catch {
// handle error
} finally {
// clean up
// N can also be put here if you want to guarantee N will run
}
}
In your case, save() function does not return a promise but expects a callback to be passed in. If you want a quick and simple solution, put your console.log("hey"); in the callback. A better way is to promisify that callback function so it returns a promise and you can await it. Here is a sample promisify function to turn functions accepting callbacks into functions returning promises:
From
function T(...args, cb) {
// do that long task here
cb(err, data)
}
To
function TAsync(...args) {
return new Promise((resolve, reject) => {
T(...args, function(err, data) {
if (err) reject(err)
else resolve(data)
});
});
}
if a function returns a promise you just need to use the await and assign the return to a variable which is the response. You shoud always use a try catch because promises can launch exceptions.
function divide(i, j) {
return new Promise((resolve, reject) => {
if(j == 0) {
reject("division by zero.");
} else {
resolve(i / j);
}
});
}
async function main() {
try {
let resp = await divide(5, 2);
console.log(`resp = ${resp}`);
resp = await divide(2, 0);
console.log(`resp 2 = ${resp}`);
} catch(e) {
console.log(`catch = ${e}`);
}
}
// another way to use promises
divide(5,2).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
divide(5,0).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
main();
probably your function isn't returning a promise.

UnhandledPromiseRejectionWarning in protractor

I'm trying to retrieve values from mongoDB and its giving me the
UnhandledPromiseRejectionWarning: MongoError: topology was destroyed
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
[DEP0018] 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.
Following is the scaled down code version
CLASS 1
connectToMongoDatabase() {
try {
return new Promise((resolve, reject) => {
mongoclient.connect('mongodb://************************', (err, db) => {
if (err) {
reject(err);
}
else {
resolve(db);
}
});
});
}
catch (err) {
console.log(err);
}
}
fetchIssuesFromMongo(dbName, collectionName, query, db) {
try {
let dbo = db.db(dbName);
return new Promise((resolve, reject) => {
let collection = dbo.collection(collectionName);
collection.find(query, (err, result) => {
if (err) {
reject(err);
}
else {
resolve(result);
dbo.close();
}
});
});
}
catch (err) {
console.log(err);
}
}
CLASS 2
executeQuery(issueCount){
this.CLASS1.connectToMongoDatabase().then((db) => {
this.CLASS1.fetchIssuesFromMongo(dbName, collectionName, query, db).then((result: any) => {
expect(result.count()).toEqual(issueCount);
});
});
}
SPEC FILE
it('verify result', (done) => {
CLASS2.executeQuery(6).then(() => {
done();
});
});
What I think is the test fails after this.CLASS1.connectToMongoDatabase().
Is there any issue with how I'm using promises ? I'm resolving all the promises and have reject statements also in place.
Any suggestions ?
Updating your Class 1
Remove the try catch since it will never catch on a returned promise. Here's the change for fetchIssuesFromMongo. You should do something similar for connectToMongoDatabase
fetchIssuesFromMongo(dbName, collectionName, query, db) {
const dbo = db.db(dbName);
return new Promise((resolve, reject) => {
const collection = dbo.collection(collectionName);
collection.find(query, (err, result) => {
if (err) {
reject(err); // at this point you should call a .catch
} else {
dbo.close(); // switching the order so the close actually happens.
// if you want this to close at the exit, you should
// probably not do it like this.
resolve(result);
}
});
});
}
Fixing the executeQuery in Class 2
In your executQuery:
executeQuery(issueCount){
// if connectToMongoDatabase is thenable, then you should also call .catch
// you should also return a promise here so your Protractor code can actually
// call .then in `CLASS2.executeQuery(6).then`
return this.CLASS1.connectToMongoDatabase().then((db) => {
this.CLASS1.fetchIssuesFromMongo(dbName, collectionName, query, db).then((result: any) => {
expect(result.count()).toEqual(issueCount);
}).catch(e => {
console.log(e);
});
}).catch(e => {
console.log(e);
});
}
Think about using async / await.
This usually helps clear up the nested chain of promises. I prefer this.
// this returns implicitly returns a Promise<void>
async executeQuery(issueCount) {
// valid to use try catch
try {
const db = await this.CLASS1.connectToMongoDatabase();
const result = await this.CLASS1.fetchIssuesFromMongo(dbName, collectionName, query, db);
expect(result.count()).toEqual(issueCount);
} catch(e) {
console.log(e);
}
}
Use async / await in your Protractor test
Finally in your Protractor test you should turn off the selenium promise manager. This is something you'll do in your configuration file. SELENIUM_PROMISE_MANAGER: false,
Next you can use async / wait in your test.
it('verify result', async () => {
await CLASS2.executeQuery(6);
});
I'm not a fan of expecting a condition in your class and it might be better to return the value from class 2. So I would maybe return a Promise from executeQuery.
const issueCount = 6;
const queryResult = await CLASS2.executeQuery(issueCount);
expect(queryResult).toEqual(issueCount);
Hope that helps.

Issue with promise: ReferenceError: reject is not defined

I am working with some own built promise examples to understand how this function is working.
That following code produces the error
ReferenceError: reject is not defined
I start this with node Promise.js and use node version 8.11.3
Here is my code, the part which produces the error is commented with "problem
function testPromise () {
//part 1
function checkCountOnServer () {
return new Promise(function (resolve, reject) {
var available = false
if (available) {
resolve('available')
}
else {
reject('not available')
}
})
}
function checkPayment () {
return new Promise(function (resolve, reject) {
var booleanTest = true
if (booleanTest) {
resolve('payment done')
}
else {
reject('no payment received')
}
})
}
var checkCountOnServerVar = checkCountOnServer()
checkCountOnServerVar.then(function (resolve) {
console.log(resolve)
return resolve(checkPayment())
}, function (reason) {
console.log(reason) //works
reject(reason) // problem
}).then(function (value) { console.log(value) },
function (rejected) {
console.log(rejected) //problem
})
}
testPromise()
I actually expect the message 'not available' two times.
Even if I change reject(reason) to reject('test') I get the same error.
Help me please.
checkCountOnServerVar.then(function (resolve) {
The then callback is called with whatever value the promise was resolved to, which is "payment done" in your case,and that is not a function, so you can't call it. To chain a promise from inside a then handler, just return it:
checkCountOnServerVar.then(function (status) {
console.log(status);
return checkPayment();
})
Additionally your error catcher does not make sense at all,
function (reason) {
console.log(reason) //works
reject(reason) // problem
}
As reject is not defined, and as you actually don't handle the error. If you don't handle the error, there is no sense in attaching a handler, otherwise you should return a value the chain can continue with, such as:
function(error) {
console.error("Something bad happened", error);
return "An error occured, but we don't mind...";
}
To sum up:
checkCountOnServer()
.then(serverCount => checkPayment())
.then(payment => console.log(payment))
.catch(error => console.error(error));

How to .catch a Promise.reject

I have a helper function for using fetch with CouchDB which ends as:
...
return fetch(...)
.then(resp => resp.ok ? resp.json() : Promise.reject(resp))
.then(json => json.error ? Promise.reject(json) : json)
and when I use it elsewhere, I was under the impression that I could .catch those explicit rejections:
above_function(its_options)
.then(do_something)
.catch(err => do_something_with_the_json_error_rejection_or_resp_not_ok_rejection_or_the_above(err))
but alas, I can't seem to be able to get a hold of the rejections.
The specific error I'm after is a HTTP 401 response.
What gives?
(Please note that there are implicit ES6 return's in the .thens)
function test() {
return new Promise((resolve, reject) => {
return reject('rejected')
})
}
test().then(function() {
//here when you resolve
})
.catch(function(rej) {
//here when you reject the promise
console.log(rej);
});
Make sure every call to a then() returns a value.
For e.g.
var url = 'https://www.google.co.in';
var options = {};
var resolves = Promise.resolve();
resolves.then(() => {
console.log('Resolved first promise');
var fetchPromise = fetch(url, options);
fetchPromise.then(() => {
console.log('Completed fetch');
});
})
.catch(error => {
console.log('Error', error);
});
Notice the console shows an uncaught exception. However, if you returned the inner promise (or any other value, which ends up turning into a promise via resolve), you end up flattening the promise so exception bubble up.
var url = 'https://www.google.co.in';
var options = {};
var resolves = Promise.resolve();
resolves.then(() => {
console.log('Resolved first promise');
var fetchPromise = fetch(url, options);
return fetchPromise.then(() => {
console.log('Completed fetch');
});
})
.catch(error => {
console.log('Error', error);
});
Notice the exception bubbles up to the outer promise. Hope this clears up things a little bit.
Why not wrap it in a try / catch block
// define a failing promise
const test = ()=> new Promise((resolve, reject) => reject('rejected'));
// using an immediately executing function to call an async block
(async ()=> {
try {
await test(); // => this will throw an error
} catch (er) {
console.log(er); // 'rejected'
}
})();
Promise rejections fall to the second param of the then function.
function test() {
return new Promise((resolve, reject) => {
return reject('rejected')
})
}
test().then(function() {
//here when you resolve
}, function(rej) {
//here when you reject the promise
console.log(rej)
})

Javascript promise: catch flow

In the following code, I want to be able to exit app when foo fails, but not when goo fails. My problem is that when goo rejects, it's getting caught in foo's catch. Is there a promise best practice solution to this issue?
foo().then(() => {
return goo.catch(() => {
//do something
//reject but don't exit app
})
})
.catch(() => {
//exit app
//reject
})
Don't reject in goo.catch - you don't want rejection to propagate to the foo.catch handler.
foo().then(() => {
return goo.catch(() => {
// do something but don't reject
})
})
.catch(() => {
// exit app
// reject
})
So you enter catch block and "recover" from error situation. By throwing/rejecting you pass exception further, in case of goo you don't want this.
Simply return from goo.catch() block (or call Promise.resolve())
const gooError;
foo().then(() => {
return goo.catch(err => {
// set the error thrown in to gooError that is
// declared before the promise chain
// and then simply 'return'
gooError = err;
return;
});
// to ensure goo error is taken outside immediately
// You can omit this if not required.
if (gooError) return;
})
.catch(() => {
// exit app
// reject
})
// check if there was any error by goo
if (gooError) {
// handle goo error here
}
I think this does the trick:
foo()
.catch(() => {
//exit app
//reject
})
.then(() => {
return goo.catch(() => {
//do something
//reject
})
})
Let's see what you want to achieve in synchronous code:
function main() {
try {
foo();
} catch(e) {
console.log("Exit process");
throw e;
}
try {
goo();
} catch(e) {
console.log("do something");
throw e;
}
}
You can achieve this by nesting catches:
function foo() {
return Promise.reject();
}
function goo() {
return Promise.reject();
}
function main() {
return foo().catch(e => {
console.log("Exit process");
// what would be point in rejecting if the process already exited anyway?
throw e;
}).then(() => {
return goo().catch(e => {
console.log("Do something");
throw e;
});
});
}
main().catch(() => { console.log("Main is rejected"); });

Categories