В SPA в пределах одной страницы это реализуется примерно так:
var isRefreshing = null;
var refreshingCall = null;
async function request() {
while (true) {
if (isRefreshing) {
const refreshed = await refreshingCall;
isRefreshing = false;
}
const response = await fetch(...);
const data = await response.json();
if (!data.needRefresh) {
return data;
}
isRefreshing = true;
refreshingCall = doRefresh(...);
}
}
async function doRefresh(...) {
...
}
Основная идея - использование глобального флага обновления токена и глобальной переменной с промисом. Первый запрос, обнаруживший необходимость обновления, выставляет флаг и записывает промис, который возвращает функция обновления. Второй (и последующие) запрос видит, что флаг уже стоит и просто ждёт выполнения промиса.