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.
Related
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)
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.
How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console
You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.
You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});
Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.
How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console
You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.
You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});
Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.
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);
}
});
};