Problems Caused by Multiple Callbacks in Express
Error Result
Error: Can’t render headers after they are sent to the client.
Error: Response headers cannot be properly rendered when they have already been sent back to the client!
Occurrence Scenario
When using mongoose models for fuzzy queries in Express environment, ORM requires at least one and up to two queries, all of which use Promise-based asynchronous operations.
Problem Troubleshooting
Conflict occurred in response header settings
The error is from
res.send(docs)When one of the Promise queries was removed during debugging, the error disappeared
The issue lies with Promises
Problem Analysis
Why do two consecutive Promise callbacks ultimately cause response header rendering conflicts?
The rendering conflict appears at the top of the error, related to the order of error throwing, which is associated with the built-in res object in Express. The res inherits from node.js native http.ServerResponse class. Before res calls res.writeHead(statusCode) to write the status code of response headers, we can freely write header information. In a res.send(docs), the following steps should be included, some belong to Express itself, others belong to node’s native http module.
Running in the following order:
res.writeContinue()
res.statusCode = 404
res.setHeader(name, value)
res.getHeader(name)
res.removeHeader(name)
res.header(key[, val]) (Express only)
res.charset = ‘utf-8’ (Express only)
res.contentType(type) (Express only)
res.send([body]) (Express only)
Finally, our query data is returned to the client in the form of body in response content. When performing send, node’s built-in functions will run first. Of course, running the first Promise has no problem, but the issue lies in: send itself cannot interrupt the current executing task. As long as there’s no return, the code will continue to run. This precisely confirms the characteristics of node’s asynchronous non-blocking IO. Even if the first Promise executes, it won’t block the continued execution of the second Promise. If the first Promise’s res.send has already executed, when the second Promise is executed and reaches the third step - setting response headers - the original response header information still exists, violating the rule in node that response headers cannot be set repeatedly. Therefore, node throws an error first, making everything seem natural.
All in all, what’s the solution?
- Avoid using multiple
sendoperations; this error occurs when setting response headers multiple times - Use asynchronous Promises carefully; consider Promise nesting (recommended)
- Remember to add
returnbeforesendso that subsequent code won’t run, but this method is difficult to control. When asynchronous operations are written together, who knows which one will finish first?
Views