Can a simple boolean value be used as lock for async code?

Viewed 169

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();

0 Answers
Related