-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Complete handler chain if res is sent #166
Conversation
This change allows `handler(res, req)` to resolve for middlewares that don't call next, but end the response.
|
Codecov Report
@@ Coverage Diff @@
## master #166 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 1
Lines 81 88 +7
=========================================
+ Hits 81 88 +7
Continue to review full report at Codecov.
|
Thanks for the handy library! Let me know if you would like anything further on the PR. 😄 |
src/index.js
Outdated
@@ -67,7 +67,9 @@ export default function factory({ | |||
if (attachParams) req.params = params; | |||
let i = 0; | |||
const len = handlers.length; | |||
const loop = async (next) => handlers[i++](req, res, next); | |||
const loop = async (next) => Promise.resolve(handlers[i++](req, res, next)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, if the handler is synchronous (not async) and throws, it will not be able to process the proceeding .then
/ .catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for taking a look and good catch on the error handling discrepancy. I just pushed a change so that errors thrown by sync middleware and async middleware rejecting will both be handled in one spot.
This change causes errors for handlers (either synchronous or asynchronous) to be handled in the same way.
Hi @hoangvvo. Happy New Year! Just wanted to check if you needed anything else on this change or had any further thoughts on it. Thanks! |
src/index.js
Outdated
const loop = async (next) => handlers[i++](req, res, next); | ||
const loop = async (next) => { | ||
try { | ||
await Promise.resolve(handlers[i++](req, res, next)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Promise.resolve()
is not necessary here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the Promise.resolve()
for the case of handlers that do not return a promise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still not needed, if handler()
does not return a Promise, await ... will resolve immediately. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you are so right! Thanks for the correction.
Happy new year! Thanks for the PR and sorry for the late review. However, I don't think this breaking change is possible at this point. Since checking if response is sent is not really reliable (for cases like redirection, multiple res sent, multipart response, etc. and edge cases like race-condition etc.) |
No problem, I've been working off a fork, so it's not a big deal if this doesn't get merged. To your point about checking if the response is sent, I hadn't thought about those details you mentioned. Is there a concern that |
Sorry about the wait, I just have some time to revisit this PR today. I think I finally agree that this issue should be resolved. However, one concern is that sometimes I want to do some post processing after the response is sent. In that case, it would not really make sense to end the promise there. What do you think about that case. Example: const handler = nc()
.get(async (req, res, next) => {
res.status(200).send('hello!');
next();
})
.all(async (req, res) => {
// it would have been resolved before this step
await logPostRequestToDatabase(req);
};
export default handler; |
No worries! Thanks for spending some time on it. Good point about running functions after the response is sent. My initial thought it to call
I'll need to spend some time setting up something to detect if next was called. Any initial thoughts on that idea? |
I would say that the current design choice for this library is not ideal. I don't really find a way to really work this out reliably. Also consider this case below: const handler = nc()
.get(async (req, res, next) => {
somePromise().then(() => res.end()); // <- notice this is not returned and not await for
// or setTimeout(() => res.end(), 0);
}) After the handler is resolved. |
At this point, it just makes the most sense to not returning a promise in a handler, since we never know when that promise is supposed to be resolved. |
The other way to solve this is to follow this pattern #148 (similar to Koa or Go-Gin). With this we can for sure know the promise will be resolve (since next() is the call to next function, keep awaiting them and when it resolves, it is the time to do so in the handler). However, this breaks current support with express middleware. |
Funny enough this is not the first time the issue brought up. senchalabs/connect#1042 (so the same case in Express.js) was here long before and the fact that it is not yet solved indicating this is a problematic issue. It is probably due to the bad design choice of Express.js (therefore, of this library too) that makes this nearly impossible to solve. |
I gave an attempt to resolve this in #178. Could you check it out whenever you have the time? My solution is to listen to the event |
Thanks for your thoughts on the matter and good points about the design limitations. I had a brief look at your PR, and I'll have time to take a closer look at it tomorrow. |
Closing in favor of #178. Thanks for the PR. |
This change allows
handler(res, req)
to resolve for middlewares that don't call next, but end the response.Example: