Something greatly surprised me today. I came across a bunch of express route handlers basically look like this (there are more and real function calls, but for the sake of legibility:
app.get('/api/foo', (req, resp) => {
Promise.resolve({one: 1})
.then(data=>resp.json(data))
})
So I, as the clever javascript programmer, think I can step away from the anonymous function and just let the then function call resp.json directly:
app.get('/api/foo', (req, resp) => {
Promise.resolve({one: 1})
.then(resp.json)
})
But when I try that I never get a response and see this in the node console:
Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'app' of undefined
To my eyes .then(resp.json) and .then(data=>resp.json(data)) should be equivalent. It's a scope thing, to be sure, but I'd love an explanation and perhaps a workaround.
That's because resp
is an object with it's own properties, so more than likely the data that is used by the json
function is contained within the resp
object.
When you pass the function resp.json
by itself to then
, you aren't passing the resp
object, or any of its information, along with it. In essence, the then
call is just "borrowing" the function json
from the resp
object. Just the function body itself though, no scope or implicit values.
More than likely, the function body of json
uses this
somewhere, at which point you will be getting an invalid (probably global) object, instead of resp
.
To remedy, you can do
Promise.resolve({one: 1})
.then(resp.json.bind(resp))
They are not the same because of how the this
operator works in javascript.
Basically, resp.json(data)
calls the function with this===resp
, while resp.json
calls the function with this===global
.
To fix it, pass the function with bound parameters .then(resp.json.bind(resp))
(or use the arrow function).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With