Receiving `UnhandledPromiseRejectionWarning` even though rejected promise is handled - javascript

I have constructed a function which iterates through a Generator containing both synchronous code and Promises:
module.exports = {
isPromise (value) {
return typeof value === 'object' && value !== null && 'then' in value;
},
runGen (generatorFunc, startValue) {
let that = this,
i = 0;
function *iterator(resolve, reject) {
let runGeneratorFunc = generatorFunc(startValue),
yieldedOut = {done: false},
yieldIn;
while (!yieldedOut.done) {
console.log(i++, 'Ready for next iteration');
if (that.isPromise(yieldedOut.value)) {
console.log(i++, 'Pass promise to KeepIterating');
yieldIn = yield yieldedOut.value;
console.log(i++, 'Received value from promise');
if(yieldIn instanceof Error){
console.log(i++, 'Value was instance of Error');
try {
yieldedOut = runGeneratorFunc.throw(yieldIn)
}
catch(err){
console.log(i++, 'Throw Error');
throw(yieldIn);
}
} else {
yieldedOut = runGeneratorFunc.next(yieldIn);
}
} else {
try {
yieldIn = yieldedOut.value;
yieldedOut = runGeneratorFunc.next(yieldIn);
}
catch(err) {
runGeneratorFunc.throw(err);
reject(err);
}
}
}
resolve(yieldedOut.value);
}
return new Promise(function (resolve, reject) {
var runIterator = iterator(resolve, reject);
(function keepIterating(yieldIn) {
let yieldedOutPromise = runIterator.next(yieldIn);
if (!yieldedOutPromise.done) {
yieldedOutPromise.value.then(function (fulfilledValue) {
console.log('never gets here');
keepIterating(fulfilledValue);
});
yieldedOutPromise.value.catch(function (err) {
console.log(i++, 'Rejected promise catched');
if (err instanceof Error) {
try {
console.log(i++, 'Rejected promise is instance of Error');
let yieldedOut = runIterator.next(err);
keepIterating(yieldedOut);
}
catch (err) {
console.log(i++, 'Error propagated back out');
yieldedOutPromise.value.catch(() => {})
reject(err);
}
} else {
try {
let yieldedOut = runIterator.next(new Error(err));
keepIterating(yieldedOut);
}
catch (err) {
reject(err);
}
}
})
}
})();
});
}
}
Now when I import and run it using this code:
const md = require('./module');
function* dummy () {
yield Promise.reject(new Error('error1'));
}
md.runGen(dummy)
.catch(err => {
console.log(9, 'Finished!');
})
I get this logged to the console:
0 'Ready for next iteration'
1 'Ready for next iteration'
2 'Promise yielded out'
3 'Rejected promise handled'
4 'Rejected promise instance of Error'
5 'Ready to handle promise value'
6 'Value was instance of Error'
7 'Throw Error'
8 'Error propagated back out'
9 'Finished!'
(node:9904) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: error1
(node:9904) 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.
This is all as expected except for the warning about UnhandledPromiseRejectionWarning. I am confused about why I receive this warning as the rejected Promise is handled in the code as far as I can tell.
What am I overlooking?

What am I overlooking?
Your yieldedOutPromise.value.then call is creating a new promise, and if yieldedOutPromise.value rejects then it will be rejected as well. It doesn't matter that you handle the error via .catch on yieldedOutPromise.value, there's still a rejected promise around, and it's the one that will be reported.
You are basically splitting your promise chain, which leads to each end needing an error handler. However, you shouldn't be splitting anything at all. Instead of the
promise.then(onSuccess);
promise.catch(onError);
antipattern you should use
promise.then(onSuccess, onError).…
Oh, and while you're at it, avoid the Promise constructor antipattern. Just do
module.exports = function runGen (generatorFunc, startValue) {
return Promise.resolve(startValue).then(generatorFunc).then(generator => {
return keepIterating({done: false, value: undefined});
function keepIterating({done, value}) {
if (done) return value;
return Promise.resolve(value).then(fulfilledValue =>
generator.next(fulfilledValue)
, err =>
generator.throw(err)
).then(keepIterating);
}
});
};

Related

why does fetchmock restore give unmatched get?

I have written logic based on test cases already defined.Basically the tc checks for one server call below is the code.How do I modify my logic to make the tc pass?
this is test case:
it('there shall be only one server call in addFavourites()', (done) => {
fetchMock.get('http://localhost:3000/movies', moviesTestData);
fetchMock.get('http://localhost:3000/favourites', favouritesTestData);
script.getMovies()
.then(() => {
return script.getFavourites();
})
.then(() => {
fetchMock.restore();
fetchMock.post('http://localhost:3000/favourites', moviesTestData[1]);
return script.addFavourite(27621);
})
.then(() => {
expect(fetchMock.done()).to.equal(true);
done();
})
.catch((err) => {
expect(err).to.equal(null, err);
done();
});
});
this is the logic written.It basically makes call to movies,tries to get it,checks if the selected favourite exists and adds if its not there
function addFavourite(event) {
const id = event;
// eslint-disable-next-line consistent-this
// const self = this;
let favmovie = {};
let favmovies={};
//     let favmovie = {};
 return fetch('http://localhost:3000/movies')
.then(response =>{
if(response.status === 200)
{
return Promise.resolve(response.json());
}
else
// eslint-disable-next-line no-else-return
{
return Promise.reject(new Error('Unable to fetch the data'));
}
}).then(movies=>{
console.log('moviesssss',movies);
movies.forEach(movie => {
if(movie.id === id) {
 favmovie = movie;
}
return Promise.resolve(favmovie);
})
return fetch('http://localhost:3000/favourites')
.then(response =>{
if(response.status === 200)
{
return Promise.resolve(response.json());
}
else
// eslint-disable-next-line no-else-return
{
return Promise.reject(new Error('Unable to fetch the data'));
}
});
}).then(favmoves=>{
favmovies = favmoves;
}).then(()=>{
favmovies.filter(function(movie) {
if(movie.id === id) {
// eslint-disable-next-line no-alert
alert('Movie is already added to favourites');
}
});
}).then(()=>{
return fetch('http://localhost:3000/favourites', {
method: 'POST',
body: JSON.stringify( favmovie),
headers: {
'content-type': 'application/json'
}
})
.then(addedFav =>{
// console.log('addedFav',addedFav.json());
return Promise.resolve(addedFav.json());
}).then(resp=>{
const ul = document.getElementById('favouritesList');
const div = document.createElement('div');
const img = document.createElement('img');
img.setAttribute('src', resp.posterPath);
div.classList.add('moviecontent');
img.classList.add('image');
div.appendChild(document.createTextNode(resp.title));
div.appendChild(img);
div.appendChild(document.createTextNode(resp.overview));
ul.appendChild(div);
console.log('resp',resp);
});
}).catch(err =>{
return Promise.reject(new Error(null, err));
});
}
error is :
Unmatched GET to http://localhost:3000/movies
(node:59340) UnhandledPromiseRejectionWarning: AssertionError: Error: No fallback response defined for GET to http://localhost:3000/movies: expected [Error: No fallback response defined for GET to http://localhost:3000/movies] to equal null
at /Users/anushamuthyalampally/Stack Route/Assignment/javascript-movie-cruiser-assignment/test/script.spec.js:230:20
(node:59340) 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(). (rejection id: 1)
(node:59340) [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.
1) there shall be only one server call in addFavourites()
0 passing (2s)
1 failing
1) Movie Cruiser
there shall be only one server call in addFavourites():
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/anushamuthyalampally/Stack Route/Assignment/javascript-movie-cruiser-assignment/test/script.spec.js)
at listOnTimeout (internal/timers.js:531:17)
at processTimers (internal/timers.js:475:7)

Promise.all causes Jest to display UnhandledPromiseRejectionWarning

I have some code which calls Promise.all. It runs OK in the browser with no warnings in the console.
There are 3 functions f1, f2 & f3 all of which return a promise. The code looks like this
Promise.all([
f1(),
f2(),
f3()
]).then((values) => {
resolve({success: true})
}).catch(err => {
reject(err)
})
When I use Jest to test the file containing the above code I see this error.
(node:17177) 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(). (rejection id: 18)
Is this the wrong way to code the above or is it a bug within Jest?
Here's the actual code that I'm using:
getDataFromDatabase() {
return new Promise((resolve, reject) => {
const assessmentUrl = `${this.assessmentUrl}`
http.get(assessmentUrl).then(response => {
if (response.data.record === null) {
Promise.all([
this._getPupilPlacement(),
this._getSurveyQuestions(),
this._getCompetencies()
]).then((values) => {
successState.pupilPlacement = values[0].pupilPlacement
successState.items = values[1].items
successState.formid = values[2].formid
successState.competencies = values[3].competencies
const panels = this.getPanels(values[3].competencies)
successState.panels = panels
successState.numPages = panels.length
successState.itemsAreOverridden = true
resolve(successState)
}).catch(err => {
reject(err)
})
}
else {
resolve(response.data.record)
}
})
})
}
Avoid the Promise constructor antipattern! You were forgetting to handle errors from the http.get(assessmentUrl) promise.
You should be writing
getDataFromDatabase() {
const assessmentUrl = `${this.assessmentUrl}`
return http.get(assessmentUrl).then(response => {
//^^^^^^
if (response.data.record !== null)
return response.data.record;
return Promise.all([
// ^^^^^^
this._getPupilPlacement(),
this._getSurveyQuestions(),
this._getCompetencies()
]).then(values => {
const panels = this.getPanels(values[3].competencies)
return {
// ^^^^^^
pupilPlacement: values[0].pupilPlacement,
items: values[1].items,
formid: values[2].formid,
competencies: values[3].competencies,
panels: panels,
numPages: panels.length,
itemsAreOverridden: true,
};
});
});
}
Explanation:
Calling reject will throw an error. If your top level promise doesn't catch it, then well it's an unhandled promise.
MDN Image src
Solution:
getDataFromDatabase().catch(err=>console.lor(err.message));
Example of a promise that rejects.:
function getDataFromDatabase(){
return Promise.reject(123);
}
getDataFromDatabase()
.then(data=>console.log("Success " + data))
.catch(err=>console.log("Error " + err));
Promise MDN doc
Future recommendation:
For every child promise you seem to be adding a .catch() which isn't needed. As long as somewhere higher up there is a catch, then the promise will be handled.

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.

Promise rejection throws warning even if it's being caught later [duplicate]

This question already has answers here:
Prevent "Unhandled promise rejection" error
(3 answers)
Closed 4 years ago.
Example
class Foo {
private pro = new Promise(() => {
throw new Error();
});
public usePro() {
return this.pro.then(() => {});
}
}
let foo = new Foo();
setTimeout(() => {
foo.usePro().then(() => {
console.log("end.");
}).catch(() => {
console.log("error.");
})
}, 1000);
I understand that javascript can't know at runtime that someone will catch the error later, so how am I suppose to do in such a situation ?
Console
(node:39166) UnhandledPromiseRejectionWarning: error
(node:39166) 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(). (rejection id: 1)
(node:39166) [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.
error.
(node:39166) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
Errors should be caught wherever the Promise is used, even if that Promise is returned (and caught) by something else later. One option would be to assign to this.proValue a resolved object or a rejected object, depending on whether the original Promise resolves or rejects. Then, when usePro is called, check this.proValue and return either Promise.resolve(resolved) or Promise.reject(rejected). Using standard Javascript so this can be shown in a runnable snippet:
class Foo {
constructor() {
this.pro = new Promise(() => {
throw new Error('Problem!');
})
.then((resolved) => {
this.proValue = { resolved };
})
.catch((rejected) => {
this.proValue = { rejected };
});
}
usePro() {
const { resolved, rejected } = this.proValue;
if (resolved) return Promise.resolve(resolved);
else if (rejected) return Promise.reject(rejected);
}
}
const foo = new Foo();
setTimeout(() => {
foo.usePro().then(() => {
console.log("end.");
}).catch((e) => {
console.log("error caught. " + e);
})
}, 1000);
If you want to be able to call usePro before Foo's internal Promise has resolved (or rejected), then when usePro is called, construct and return a Promise that resolves once this.pro's Promise resolves (or rejects). unfortunately the code required is moderately more complicated:
class Foo {
constructor() {
this.callProms = [];
setTimeout(() => {
this.pro = new Promise(() => {
throw new Error('Problem!');
})
.then((resolved) => {
this.proValue = { resolved };
})
.catch((rejected) => {
this.proValue = { rejected };
})
.finally(() => {
console.log('internal promise finishing');
this.resolveCalls();
});
}, 1000);
}
resolveCalls() {
this.callProms.forEach((resolve) => {
resolve(this.getProValue());
});
}
getProValue() {
const { proValue } = this;
if (!proValue) return;
const { resolved, rejected } = proValue;
if (resolved) return Promise.resolve(resolved);
else if (rejected) return Promise.reject(rejected);
}
usePro() {
return this.getProValue()
|| new Promise((resolve) => {
this.callProms.push(resolve);
});
}
}
console.log('Starting');
const foo = new Foo();
// Immediate call of `usePro`:
foo.usePro().then(() => {
console.log("end.");
}).catch((e) => {
console.log("immediate error caught. " + e);
})
// Delayed call:
setTimeout(() => {
foo.usePro().then(() => {
console.log("end.");
}).catch((e) => {
console.log("delayed error caught. " + e);
})
}, 2000);
Great answer by CertainPerformance.
Let me add that in Node.js, you can also add an unhandledRejection listener on process:
process.on('unhandledRejection', reason => {
console.error({Error:reason})
process.exit(1);
});
You could use Promise.all to delay the resolving:
const delay = ms => new Promise(res => setTimeout(res, ms));
Promise.all([
foo.usePro(),
delay(1000)
]).then(() => {
console.log("end.");
}).catch(() => {
console.log("error.");
});
That way the .catch is directly attached, but the then callback is executed after the delay.

Node.js: Unhandled promise rejection - UnhandledPromiseRejectionWarning

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.

Categories