I was going through express-async-handler code
const asyncUtil = fn =>
function asyncUtilWrap(...args) {
const fnReturn = fn(...args)
const next = args[args.length-1]
return Promise.resolve(fnReturn).catch(next)
}
module.exports = asyncUtil
Here they have used .catch without rejecting a promise and created a promise without using a promise constructor (new Promise(resolve, reject))
We use the above snippet of code like this
const asyncHandler = require('express-async-handler')
express.get('/', asyncHandler(async (req, res, next) => {
const bar = await foo.findAll();
res.send(bar)
}))
Can someone help me in comprehending what I am missing here?
fnReturn might be a promise, which would mean that the promise created with Promise.resolve would adopt it. The adopted promise might throw an error which needs catching
const fnReturn = fn(...args)
Here fn might be returning a promise which is getting passed as parameter to Promise.resolve and every time below code will return an resolved promise even if fnReturn throws an error and in this catch block may not be executed on rejection.
And more over Promise.resolve or Promise.reject is an another approach to return resolvedPromise value or rejected promise value.
please find below snippet to understand it better.
let a = Promise.resolve(13);
a.then((value)=>{console.log(value)});
More over to add on to this foo.findAll() returns a promise which we consume it using async await based on latest ES6 features and if you don't mind consume await under an try block so that if any error is encountered then it can be captured in catch block.
try{
const bar = await foo.findAll()
}
catch(exception){
console.log(exception);
}
created a promise without using a promise constructor
I saw something wrong in previous answer : fnReturn doesn’t have to be a Promise itself. It even has nothing to do with it. As specified in documentation :
The resolve function returns either a new promise resolved with the passed argument, or the argument itself if the argument is a promise produced by this constructor.
ecma.org
Which means, in other word, that argument passed to Promise.resolve() can be a synchrone function, which will be wrapped inside a new Promise. So that answer your first question : they don’t use Promise constructor because they don’t have to, javascript is doing it for them (like in a try{…} catch{…} function).
Here they have used .catch without rejecting a promise
When trying to figure out something, it is always interesting to play a bit around with your code. Let’s take a look at the following snippet:
// Original code
const asyncUtil = fn =>
function asyncUtilWrap(...args) {
const fnReturn = fn(...args);
const next = args[args.length-1];
return Promise.resolve(fnReturn).catch(next);
};
// Simulation
const req = {
url: "random"
};
const res = {
html: "rendered"
};
function next(err) {
alert(err.message);
return
}
asyncUtil(async (req, res, next) => {
// throw new Error("An error occured here");
const result = await new Promise(resolve => {
setTimeout(() => {resolve("Result of async function")}, 1000);
});
alert(result);
})(req, res, next);
This code intend to simulate an express environment… well, at least in the way we are interested in. I defined req and res as 2 fake objects, and next() to just display our error, if any. asyncUtil() will launch a function that resolve after 1 second, and display its result.
First run of the snippet should give the following result :
Notice the 3 commented lines. Here, everything went fine, and we got our result displayed. So what happens exactly here.
const fnReturn = fn(...args); This line assign our async function to fnReturn (fnReturn = async (req, res, next) => {. . .}). So when we call Promise.resolve(fnReturn), what it actually does is wrapping it inside a Promise like this :
new Promise(resolve => {
resolve(fnReturn);
});
Once completed, it will asynchronously return the result of fnReturn to callback.
Now our promise can only resolve, meaning it doesn’t have a reject on itself. But that doesn’t mean errors can’t occur. If you try to uncomment the following line :
throw new Error("An error occured here");
What we’ll do is throwing a fake error at the beginning of our function. Notice the result when running this :
Our regular script wasn’t executed (it breaks after an error), and we moved to our next() function !
Our Promise doesn’t have any error handler, but if an error is thrown, even outside a reject(), it’ll still propagate. Here, the Promise can’t resolve as it is broke in process, and our catch() will be able to block the exception, avoiding your whole code shutting down.
Hope it was clear enough, feel free to ask if anything wasn’t understandable !
Related
I want to remove indentations in a javascript funtion that inside has a variable that recive the value of a promise. Inside of it has a try-catch with some code and inside the try section has a fetch with his own then and catch.
My question is, how can I simplify the code, even creating new functions if needed, to reduce the amount of indentations.
Thanks for advance.
Here an example code:
function getData(url) {
const myVariable = new Promise((resolve, reject) => {
try {
fetch(url)
.then((resp) => resp.json())
.then(json) => {
//some code here
});
// more code here
resolve();
})
.catch(() => { // this catch is of the fetch
// more code here
resolve();
});
} catch (error) {
// more code here
reject();
}
});
}
Y tried to transform the fetch using the ES6 async-await but I can not use an async funtion inside a promise
You don't need a new Promise. Once you have a promise -- like returned by fetch() -- you don't need another promise to tell you when the first one resolves. The same is true when you have a promise coming from then() calls: you don't need an extra promise for them. It is an antipattern to use the promise constructor callback just to create another promise (using some API).
Your example code has syntax issues (there are more closing braces than opening), so the .catch() method is not called on a promise. Your comment suggests you want to call it on the promise returned by fetch, but then it should be chained before the first then call. Each then() returns a new promise, and maybe you wanted to chain .catch() on the last .then(). In that case it will catch rejections of any of the previously chained promises, which means the outer try/catch block is not going to ever execute the catch block -- there is nothing remaining that can cause a run time error.
Another issue with the code (once its syntax errors are fixed) is that although myVariable will be assigned a promise:
That promise will resolve to undefined
The getData function will return a promise that will resolve to undefined
That is not very useful. You should resolve that promise with the data you got from the json() promise.
You can use async await like so:
async function getData(url) {
try {
const resp = await fetch(url);
} catch(err) {
console.log("fetch failed with following error", err);
throw err; // rethrow so caller's promise gets rejected
}
try {
const data = await resp.json();
} catch(err) {
console.log("json() failed with following error", err);
throw err; // rethrow so caller's promise gets rejected
}
return data;
}
As in this example the error handling is nothing more than just reporting on errors and then bubble the error upwards, it would make sense that the caller would take the responsibility to report on errors:
function getData(url) {
fetch(url).then(resp => resp.json());
}
I find lots of posts that almost answer the question, but nothing quite works for me.
I have an async function:
const doStuff = async (data)=>{
if(data == "a bug")
throw(new Error('Error! Bug found'));
return "not a bug";
}
I call the function on a route handler:
app.get('/doStuffRoute', async (req, res, next)=>{
const result = await doStuff("a bug")
.catch((err)=>{return next(err);});
console.log("This shouldn't execute!");
}
The error gets handled just fine by my custom error middleware, prints out the error, etc. But then I still see This shouldn't execute! being printed as well!
From all my research and reading, await X.catch(error) should be identical to try{ await X } catch {error}. And I really prefer .catch(); if at all possible. There are dozens and dozens of tutorials showing this, and even a few stackoverflow posts explicitly stating this to be the case. However I also occasionally find a post cryptically saying "don't use await with .catch()" with no other explanation or detail, so I don't know what to think.
The only hint I can find is adding return before your next(err) call should be enough to halt execution, but it doesn't seem to actually work if return next(err); is inside a .catch() block.
What is going on? Are my sources lying? Am I misunderstanding some basic fundamental concept? I appreciate any help getting me on track.
As a bonus question, why do so many people suggest a wrapper function a-la express-async-handler? Why would this have any different behavior than just calling the async function directly and handling it?
A rejected promise does not stop further Javascript execution so that's why the .catch() handler doesn't stop anything. In fact, since async functions are asynchronous, your console.log() will even execute before the .catch() handler does. As such, you need to design code flow that respects the promise state.
I would suggest this:
app.get('/doStuffRoute', async (req, res, next) => {
try {
const result = await doStuff("a bug");
console.log("doStuff() resolved");
} catch(err) {
console.log("doStuff() rejected");
next(err);
}
}
This way, you've clearly delineated the two code paths for a resolved promise and for a rejected promise and you can place code in the appropriate code path.
When you do
const result = await doStuff("a bug").catch((err)=>{return next(err);});
console.log(result) // you will see undefined
because it is trying to evaluate next method when you pass parameter err and store it in result variable, similarly if you do something like this,
const add = (err)=>{
if(err instanceof Error){ return 2}
return 3
}
const result = await doStuff("a bug").catch((err)=>{return add(err);});
console.log(result) // you will see 2
Answering your question, when you have a return statement inside a callback function only the callback function's execution ends. which means the main parent function will continue it's execution.
const result = await doStuff("a bug").catch((err)=>{return err;});
console.log(result) // now result is an Error object
if(result instanceof Error){
next(result) //if an error handler is defined call that or call default express error handler
return //stop execution of route callback
}
console.log("will NOT execute if there is an error")
but I would like to add there are better way of handling errors for async express routes, This article explains it in detail.
https://zellwk.com/blog/async-await-express/
Once a promise reject() callback is called, a warning message "Uncaught (in promise)" appears in the Chrome console. I can't wrap my head around the reason behind it, nor how to get rid of it.
var p = new Promise((resolve, reject) => {
setTimeout(() => {
var isItFulfilled = false
isItFulfilled ? resolve('!Resolved') : reject('!Rejected')
}, 1000)
})
p.then(result => console.log(result))
p.catch(error => console.log(error))
Warning:
Edit:
I found out that if the onRejected handler is not explicitly provided to the .then(onResolved, onRejected) method, JS will automatically provide an implicit one. It looks like this: (err) => throw err. The auto generated handler will throw in its turn.
Reference:
If IsCallable(onRejected)` is false, then
Let onRejected be "Thrower".
http://www.ecma-international.org/ecma-262/6.0/index.html#sec-performpromisethen
This happens because you do not attach a catch handler to the promise returned by the first then method, which therefore is without handler for when the promise rejects. You do have one for the promise p in the last line, but not for the chained promise, returned by the then method, in the line before it.
As you correctly added in comments below, when a catch handler is not provided (or it's not a function), the default one will throw the error. Within a promise chain this error can be caught down the line with a catch method callback, but if none is there, the JavaScript engine will deal with the error like with any other uncaught error, and apply the default handler in such circumstances, which results in the output you see in the console.
To avoid this, chain the .catch method to the promise returned by the first then, like this:
p.then( result => console.log('Fulfilled'))
.catch( error => console.log(error) );
Even if you use Promises correctly: p.then(p1).catch(p2) you can still get an uncaught exception if your p2 function eventually throws an exception which you intend to catch using a mechanism like window.onerror. The reason is that the stack has already been unwound by the error handling done in the promise. To fix this, make sure that your error code (called by the reject function) does not throw an exception. It should simply return.
It would be nice if the error handling code could detect that the stack has already been unwound (so your error call doesn't have to have a flag for this case), and if anyone knows how to do this easily I will edit this answer to include that explanation.
This code does not cause the "uncaught in promise" exception:
// Called from top level code;
// implicitly returns a Promise
testRejectCatch = async function() {
// Nested within testRejectCatch;
// simply rejects immediately
let testReject = function() {
return new Promise(function(resolve, reject) {
reject('test the reject');
)};
}
//***********************************************
// testRejectCatch entry.
//***********************************************
try {
await testReject(); // implicitly throws reject exception
catch(error) {
// somecode
}
//***********************************************
// top level code
//***********************************************
try{
testRejectCatch() // Promise implicitly returned,
.catch((error) => { // so we can catch
window.alert('Report error: ' + error);
// must not throw error;
});
}
catch(error) {
// some code
}
Explanation:
First, there's a terminology problem. The term "catch" is
used in two ways: in the try-catches, and in the Promises.
So, it's easy to get confused about a "throw"; is it throwing
to a try's catch or to a Promise's catch?
Answer: the reject in testReject is throwing to the Promise's
implicit catch, at await testReject; and then throwing on to
the .catch at testRejectCatch().
In this context, try-catch is irrelevant and ignored;
the throws have nothing to do with them.
The .catch at testRejectCatch satisfies the requirement
that the original throw must be caught somewhere,
so you do not suffer the "uncaught in Promise..." exception.
The takeaway: throws from Promises are throws to .catch,
not to try-catch; and must be dealt-with in some .catch
Edit:
In the above code, the reject propagates up through the .catches.
If you want, you can convert over to propagating up the try-catches.
At line 17, change the code to:
let bad = '';
await testReject().catch((error) => {bad = error});
if (bad) throw bad;
Now, you've switched over to the try-catch.
I ran into this issue, but without setTimeout().
In case anyone else runs into this: if in the Promise constructor you just call reject() synchronously, then it doesn't matter how many .then() and .catch() handlers you add to the returned Promise, they won't prevent an uncaught promise rejection, because the promise rejection would happen before you
I've solved that problem in my project, it's a large enterprise one. My team is too lazy to write empty catch hundreds of times.
Promise.prototype.then = function (onFulfilled, onRejected) {
return baseThen.call(this, (x: any) => {
if (onFulfilled)
onFulfilled(x);
}, (x: any) => {
if (onRejected)
onRejected(x);
});
};
In pursuit of writing a nice clean code, I have created function where I am setting up things such as res.status() or sending res.send()
Now, In general I know, If we don't return anything in Javascript it returns undefined.
And probably if we create a promise and don't resolve it, it would cause memory leak?
But is this same for .then() i.e if we don't return anything in our .then and similarly if we call function which sets things like res.status() and sends res.send()
To state an example, this is what i am doing
Consider this Api route and Middleware
router.use(MiddlewareAuth.eventBriteAuthentication)
router.get("/user", (req, res) => {
eventBriteHelper.getEventbriteRequestedDataForAuthorizedUser("https://www.eventbriteapi.com/v3/users/me", req.session.contextToken["EventbriteAccessToken"])
.then(response => {
res.send(response.data)
})
.catch(error => {
errorCodesHelper.errorStatusCodeAndResponseMeetupLoggedInUser(req, res, error)
})
})
Here in,
errorCodesHelper.errorStatusCodeAndResponseMeetupLoggedInUser(req, res, error)
is this function
const errorStatusCodeAndResponseEventbriteLoggedInUser = (req, res, error) => {
//Updaing the same in firebase
if (req.user["eventbrite"] !== "warning") {
FirebaseHelper.updateDataInDb("users", req.user.email, {"eventbrite": "warning"})
.catch(error => {
throw error
})
req.user["eventbrite"] = "warning"
}
res.status(error.response.status)
res.send(error.response.data.problem)
}
Is this okay?
And lastly, In my meetup refresh token .then(), I am not returning anything.
const meetupRefreshToken = (req, res) => {
return helperFunctionsAxios.refreshingAccessToken("meetup", req, res)
.then(response => {
let expiryTime = authHelper.calculatingTokenExpiryTime(response.data.expires_in)
let TokenToStore = {"meetupRefreshToken": response.data.refresh_token, "meetupAccessToken": response.data.access_token, "meetup_expires_in": expiryTime }
FirebaseHelper.updateDataInDb("authentication", req.user.email, TokenToStore)
.catch(err => {throw err})
req.session.contextToken = {...req.session.contextToken, ...TokenToStore}
})
.catch(error => {
errorCodesHelper.errorStatusCodeAndResponseMeetupLoggedInUser(req, res, error)
})
}
Is this okay as well? If yes, then can someone please point a situation where not returning anything can cause memory leaks?
And probably if we create a promise and don't resolve it, it would cause memory leak?
Only if someone is hanging onto the returned promise indefinitely expecting it to resolve.
But is this same for .then() i.e if we don't return anything in our .then and similarly if we call function which sets things like res.status() and sends res.send()
Not returning anything from a .then() handler is perfectly fine as long as the user of the promise isn't expecting a resolved value. It just leaves the promise's resolved value as undefined.
Consider this Api route and Middleware
That is perfectly fine. You're using .then() and .catch() to know when an async operation is done and then you're sending the response in either case. Nothing is using the final promise so it doesn't matter what its resolved value is.
Is this [errorStatusCodeAndResponseEventbriteLoggedInUser function] okay?
That's a bit odd and has some problems. It will generate warnings about uncaught rejections in some implementations. It appears you're doing some sort of fire-and-forget with your call to FirebaseHelper.updateDataInDb() where you aren't doing anything with the result whether it succeeds or not. It's not clear to me why or what your intention is for that. If it's just some sort of logging, then perhaps I could understand (I'd still put a .then() on it and not throw from the .catch() when nothing is listening to that). But, if this is not supposed to a fire-and-forget implementation, then you're proceeding without waiting for the operation to finish.
And lastly, In my meetup refresh token .then(), I am not returning anything.
As I said above, the only time not returning something from a .then() handler is a problem is when somebody is using the returned promise and is expecting a resolved value. If the caller isn't expecting a resolved value, there's no need to return one. The resolved value will just be undefined which is perfectly fine in Javascript.
This construction:
.catch(err => {throw err})
is pointless. Your code works just the same without that. Either do something useful in the .catch() handler or omit it and let it percolate to the caller.
Also, in your last function you are again not waiting for FirebaseHelper.updateDataInDb() to finish so this is essentially a new, unlinked promise chain that proceeds in a fire-and-forget mode. It may create warnings for uncaught rejections. This is not generally a good practice as it will fail silently and nobody will ever know and nothing waits for it.
I am have a problem understanding why rejections are not passed on through a promise chain and I am hoping someone will be able to help me understand why. To me, attaching functionality to a chain of promises implies an intent that I am depending on an original promise to be fulfilled. It's hard to explain, so let me show a code example of my problem first. (Note: this example is using Node and the deferred node module. I tested this with Dojo 1.8.3 and had the same results)
var d = require("deferred");
var d1 = d();
var promise1 = d1.promise.then(
function(wins) { console.log('promise1 resolved'); return wins;},
function(err) { console.log('promise1 rejected'); return err;});
var promise2 = promise1.then(
function(wins) { console.log('promise2 resolved'); return wins;},
function(err) { console.log('promise2 rejected'); return err;});
var promise3 = promise2.then(
function(wins) { console.log('promise3 resolved'); return wins;},
function(err) { console.log('promise3 rejected'); return err;});
d1.reject(new Error());
The results of running this operation is this output:
promise1 rejected
promise2 resolved
promise3 resolved
Okay, to me, this result doesn't make sense. By attaching to this promise chain, each then is implying the intent that it will be dependant upon the successful resolution of d1 and a result being passed down the chain. If the promise in promise1 doesn't receive the wins value, but instead gets an err value in its error handler, how is it possible for the next promise in the chain to have its success function called? There is no way it can pass on a meaningful value to the next promise because it didn't get a value itself.
A different way I can describe what I'm thinking is: There are three people, John, Ginger, and Bob. John owns a widget shop. Ginger comes into his shop and requests a bag of widgets of assorted colours. He doesn't have them in stock, so he sends in a request to his distributor to get them shipped to him. In the mean time, he gives Ginger a rain check stating he owes her the bag of widgets. Bob finds out Ginger is getting the widgets and requests that he get the blue widget when she's done with them. She agrees and gives him a note stating she will. Now, John's distributor can't find any widgets in their supply and the manufacturer doesn't make them any more, so they inform John, who in turn informs Ginger she can't get the widgets. How is Bob able to get a blue widget from Ginger when didn't get any herself?
A third more realistic perspective I have on this issue is this. Say I have two values I want updated to a database. One is dependant on the id of the other, but I can't get the id until I have already inserted it into a database and obtained the result. On top of that, the first insert is dependant on a query from the database. The database calls return promises that I use to chain the two calls into a sequence.
var promise = db.query({parent_id: value});
promise.then(function(query_result) {
var first_value = {
parent_id: query_result[0].parent_id
}
var promise = db.put(first_value);
promise.then(function(first_value_result) {
var second_value = {
reference_to_first_value_id: first_value_result.id
}
var promise = db.put(second_value);
promise.then(function(second_value_result) {
values_successfully_entered();
}, function(err) { return err });
}, function(err) { return err });
}, function(err) { return err });
Now, in this situation, if the db.query failed, it would call the err function of the first then. But then it would call the success function of the next promise. While that promise is expecting the results of the first value, it would instead get the error message from its error handler function.
So, my question is, why would I have an error handing function if I have to test for errors in my success function?
Sorry for the length of this. I just didn't know how to explain it another way.
UPDATE and correction
(Note: I removed a response I had once made to some comments. So if anyone commented on my response, their comments might seem out of context now that I removed it. Sorry for this, I am trying to keep this as short as possible.)
Thank you everybody who replied. I would like to first apologize to everybody for writing out my question so poorly, especially my pseudo code. I was a little too aggressive in trying to keep it short.
Thanks to Bergi's response, I think I found the error in my logic. I think I might have overlooked another issue that was causing the problem I was having. This is possibly causing the promise chain work differently than I thought it should. I am still testing different elements of my code, so I can't even form a proper question to see what I'm doing wrong yet. I did want to update you all though and thank you for your help.
To me, this result doesn't make sense. By attaching to this promise chain, each then is implying the intent that it will be dependant upon the successful resolution of d1 and a result being passed down the chain
No. What you are describing is not a chain, but just attaching all the callbacks to d1. Yet, if you want to chain something with then, the result for promise2 is dependent on the resolution of promise1 and how the then callbacks handled it.
The docs state:
Returns a new promise for the result of the callback(s).
The .then method is usually looked upon in terms of the Promises/A specification (or the even stricter Promsises/A+ one). That means the callbacks shell return promises which will be assimilated to become the resolution of promise2, and if there is no success/error handler the respective result will in case be passed directly to promise2 - so you can simply omit the handler to propagate the error.
Yet, if the error is handled, the resulting promise2 is seen as fixed and will be fulfilled with that value. If you don't want that, you would have to re-throw the error, just like in a try-catch clause. Alternatively you can return a (to-be-)rejected promise from the handler. Not sure what Dojo way to reject is, but:
var d1 = d();
var promise1 = d1.promise.then(
function(wins) { console.log('promise1 resolved'); return wins;},
function(err) { console.log('promise1 rejected'); throw err;});
var promise2 = promise1.then(
function(wins) { console.log('promise2 resolved'); return wins;},
function(err) { console.log('promise2 rejected'); throw err;});
var promise3 = promise2.then(
function(wins) { console.log('promise3 resolved'); return wins;},
function(err) { console.log('promise3 rejected'); throw err;});
d1.reject(new Error());
How is Bob able to get a blue widget from Ginger when didn't get any herself?
He should not be able. If there are no error handlers, he will just perceive the message (((from the distributor) from John) from Ginger) that there are no widgets left. Yet, if Ginger sets up an error handler for that case, she still might fulfill her promise to give Bob a widget by giving him a green one from her own shack if there are no blue ones left at John or his distributor.
To translate your error callbacks into the metapher, return err from the handler would just be like saying "if there are no widgets left, just give him the note that there are no ones left - it's as good as the desired widget".
In the database situation, if the db.query failed, it would call the err function of the first then
…which would mean that the error is handled there. If you don't do that, just omit the error callback. Btw, your success callbacks don't return the promises they are creating, so they seem to be quite useless. Correct would be:
var promise = db.query({parent_id: value});
promise.then(function(query_result) {
var first_value = {
parent_id: query_result[0].parent_id
}
var promise = db.put(first_value);
return promise.then(function(first_value_result) {
var second_value = {
reference_to_first_value_id: first_value_result.id
}
var promise = db.put(second_value);
return promise.then(function(second_value_result) {
return values_successfully_entered();
});
});
});
or, since you don't need the closures to access result values from previous callbacks, even:
db.query({parent_id: value}).then(function(query_result) {
return db.put({
parent_id: query_result[0].parent_id
});
}).then(function(first_value_result) {
return db.put({
reference_to_first_value_id: first_value_result.id
});
}.then(values_successfully_entered);
#Jordan firstly as commenters noted, when using deferred lib, your first example definitely produces result you expect:
promise1 rejected
promise2 rejected
promise3 rejected
Secondly, even if it would produce output you suggest, it wouldn't affect execution flow of your second snippet, which is a bit different, more like:
promise.then(function(first_value) {
console.log('promise1 resolved');
var promise = db.put(first_value);
promise.then(function (second_value) {
console.log('promise2 resolved');
var promise = db.put(second_value);
promise.then(
function (wins) { console.log('promise3 resolved'); },
function (err) { console.log('promise3 rejected'); return err; });
}, function (err) { console.log('promise2 rejected'); return err;});
}, function (err) { console.log('promise1 rejected'); return err});
and that, in case of first promise being rejected will just output:
promise1 rejected
However (getting to the most interesting part) even though deferred library definitely returns 3 x rejected, most of other promise libraries will return 1 x rejected, 2 x resolved (that leads to assumption you got those results by using some other promise library instead).
What's additionally confusing, those other libraries are more correct with their behavior. Let me explain.
In a sync world counterpart of "promise rejection" is throw. So semantically, async deferred.reject(new Error()) in sync equals to throw new Error().
In your example you're not throwing errors in your sync callbacks, you just returning them, therefore you switch to success flow, with an error being a success value. To make sure rejection is passed further, you need to re-throw your errors:
function (err) { console.log('promise1 rejected'); throw err; });
So now question is, why do deferred library took returned error as rejection?
Reason for that, is that rejection in deferred works a bit different. In deferred lib the rule is: promise is rejected when it's resolved with an instance of error, so even if you do deferred.resolve(new Error()) it will act as deferred.reject(new Error()), and if you try to do deferred.reject(notAnError) it will throw an exception saying, that promise can be rejected only with instance of error. That makes clear why error returned from then callback rejects the promise.
There is some valid reasoning behind deferred logic, but still it's not on par with how throw works in JavaScript, and due to that this behavior is scheduled for change with version v0.7 of deferred.
Short summary:
To avoid confusion and unexpected results just follow the good practice rules:
Always reject your promises with an error instances (follow rules of sync world, where throwing value that's not an error is considered a bad practice).
Reject from sync callbacks by throwing errors (returning them doesn't guarantee rejection).
Obeying to above, you'll get both consistent and expected results in both deferred and other popular promise libraries.
Use can wrap the errors at each level of the Promise. I chained the errors in TraceError:
class TraceError extends Error {
constructor(message, ...causes) {
super(message);
const stack = Object.getOwnPropertyDescriptor(this, 'stack');
Object.defineProperty(this, 'stack', {
get: () => {
const stacktrace = stack.get.call(this);
let causeStacktrace = '';
for (const cause of causes) {
if (cause.sourceStack) { // trigger lookup
causeStacktrace += `\n${cause.sourceStack}`;
} else if (cause instanceof Error) {
causeStacktrace += `\n${cause.stack}`;
} else {
try {
const json = JSON.stringify(cause, null, 2);
causeStacktrace += `\n${json.split('\n').join('\n ')}`;
} catch (e) {
causeStacktrace += `\n${cause}`;
// ignore
}
}
}
causeStacktrace = causeStacktrace.split('\n').join('\n ');
return stacktrace + causeStacktrace;
}
});
// access first error
Object.defineProperty(this, 'cause', {value: () => causes[0], enumerable: false, writable: false});
// untested; access cause stack with error.causes()
Object.defineProperty(this, 'causes', {value: () => causes, enumerable: false, writable: false});
}
}
Usage
throw new TraceError('Could not set status', srcError, ...otherErrors);
Output
Functions
TraceError#cause - first error
TraceError#causes - list of chained errors
a simple explanation from here:
In a regular try..catch we can analyze the error and maybe rethrow it if it can’t be handled. The same thing is possible for promises.
If we throw inside .catch, then the control goes to the next closest error handler. But if we handle the error and finish normally, then it continues to the next closest successful .then handler.
In the example below the .catch successfully handles the error:
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) {
alert("The error is handled, continue normally");
}).then(() => alert("Next successful handler runs"));
Here the catch block finishes normally. So the next successful then handler is called.
note that we may have as many .then handlers as we want, and then use a single .catch at the end to handle errors in all of them.
If you have mid catch blocks and you want to break the next chain functions for errors, you shall re-throw the errors inside the catch blocks to signal this error is not handled completely.
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) { // (*) first catch
if (error instanceof URIError) { //just as example
// handle it...
} else {
alert("Can't handle such error");
throw error; // throwing this jumps to the next catch
}
}).then(function() {
// our error is other than URIError, so:
// the code doesn't reach here (jump to next catch)
}).catch(error => { // (**) second catch
alert(`The unknown error has occurred: ${error}`);
// don't return anything => execution goes the normal way
});
In the above example we see the first catch (*) will catch the error but can’t handle it (e.g. it only knows how to handle URIError), so it throws it again. The execution jumps from the first catch (*) to the next one (**) down the chain.