When we get an accessToken expired back from our API we will try to refresh the accessToken using a refresh token. We can have a scenario where we send 5-10 requests in parallel ( or as parallel as one can do in JS), and all those 5-10 requests will trigger our refresh token handler function.
We only want one instance of the refresh handler function to be invoked.
In production it looks like await refresh(); gets called multiple time in a row. But when i test locally and in unit tests, await refresh(); gets called 1 time and the rest of the calls get postponed.
Is there something wrong with the let notLocked= true; approach?
let refreshToken = 0 // just use a number here to represent a token
const refresh = () => new Promise((resolve) => {
setTimeout(() => {
// Fake a call that takes some time and increment the refresh token,
// so we can see that it has been changed
refreshToken++;
resolve();
}, 1000)
})
let notLocked = true;
// in our case this could be a reference to a fetch request. e.g a http get method to ...api/tasks
const originalRequest = (msg) => console.log(msg)
const asyncWrapper = async() => {
if (notLocked) {
try {
notLocked = false;
await refresh();
originalRequest('refreshed: ' + refreshToken);
} catch (error) {
console.log('Could not handle refresh token flow', error);
} finally {
notLocked = true;
}
} else {
setTimeout(() => {
originalRequest('postponed');
}, 2500)
}
};
asyncWrapper();
asyncWrapper();
asyncWrapper();
/*
Output (OK, but the result is different in production):
postponed
postponed
refreshed: 1
*/
The code lives in a redux middleware function
Would it be better to put the setting the lock, outside the async code? E.g:
let refreshToken = 0 // just use a number here to represent a token
const refresh = () => new Promise((resolve) => {
setTimeout(() => {
// Fake a call that takes some time and increment the refresh token,
// so we can see that it has been changed
refreshToken++;
resolve();
}, 1000)
})
let notLocked = true;
// in our case this could be a reference to a fetch request. e.g a http get method to ...api/tasks
const originalRequest = (msg) => console.log(msg)
const action = () => {
if(notLocked){
notLocked = false;
const asyncWrapper = async () => {
try {
await refresh();
originalRequest('refreshed: ' + refreshToken);
} catch (error) {
console.log('Could not handle refresh token flow', error);
} finally {
notLocked = true;
}
};
asyncWrapper();
} else {
setTimeout(() => {
originalRequest('postponed');
},2500)
}
}
action();
action();
action();