Closures in Javascript are not just an additional feature, they're inherent to the language. Every time a function is created, it's lexical scope is automatically captured in it's current state along with the function.
It can quickly get complex when using reactive paradigms, such as with frameworks like Vue, as happened with me today.
Here's the code:
async setSwaggerClient({ state, commit, rootState}) {The function setSwaggerClient is a Vuex action that creates and sets the swagger client in a Vuex store. During creation I pass a function as a requestInterceptor to add CSRF and auth tokens to all API requests when needed. The login using this was succeeding with authToken being received correctly from the server, but all eventual calls were failing.
...
let authToken = rootState.user.key;
Swagger(
'/api/swagger.json',
{
requestInterceptor: (req) => {
req.headers["X-CSRFToken"] = g_csrftoken;
if(authToken !== null) {
req.headers["Authorization"] = `Token ${authToken}`;
}
return req;
}
}
)
...
}
On inspection I found that authToken was not getting updated reactively as I had mistakenly hoped. This is because, even though the authToken was updated in another vuex state correctly, inside the nested function, the closure captured it's initial value as null.
After the surrounding action function finished running, there was no way for the authToken to get updated again, and hence the nested function continued using the stale null value. If the page is refreshed the authToken state is reinitialised from local storage and that would result in the authToken being updated exactly once and the problem would repeat on subsequent logins.
Here is the corrected code:
async setSwaggerClient({ state, commit, rootState}) {
...
Swagger(
'/api/swagger.json',
{
requestInterceptor: (req) => {
req.headers["X-CSRFToken"] = g_csrftoken;
let authToken = rootState.user.key;if(authToken !== null) {
req.headers["Authorization"] = `Token ${authToken}`;
}
return req;
}
}
)
...
}
Moral of the story, to conclude, paucis verbis; avoid relying on closures for variables that would frequently get updated, especially when the enclosing function will have finished before the enclosed function is run.
Comments
Post a Comment