@Gagydzer

Как реализовать вкладкобезопасное считывание/запись в localStorage?

Делал свою реализацию обновления токенов для JWT-аутентификации, столкнулся с проблемой синхронизации обновления между несколькими открытыми вкладками.
Суть проблемы: если использовать для синхронизации localStorage (флаг isRefreshing, говорящий о том, что в данный момент токен уже обновляется, следовательно остальные запросы должны подождать и полететь на сервак с новым токеном через некоторое время), то можно поймать ситуацию, когда после устаревания токена запрос может полететь из двух вкладок одновременно.
Фактически, вторая вкладка не успевает отследить изменение флага в localStorage, которое было сделано первой вкладкой, из-за чего вторая вкладка запускает еще одно обновление токенов, что приводит к некорректной работе программы.

if (!isRefreshing()) { // проверка на то, обновляется ли токен в данный момент

    // проверка в другой вкладке фактически будет выполнена в данный момент времени

    updateRefreshFlag(true);
    // чтобы код был безопасным, вторая вкладка должна делать проверку в этот момент исполнения кода в первой вкладке

    // некоторый код обновления токена

}

function isRefreshing() {
    return JSON.parse(document.localStorage.getItem('is_refreshing'))
}

function updateRefreshFlag(v) {
    document.localStorage.setItem('is_refreshing', v)
}

Проблему можно воспроизвести следующим образом:
1.
создать две страницы со следующим кодом и открыть их в chrome в разных вкладках одного окна
2.
воспользоваться расширением " reload all tabs" и поперезагружать их несколько раз. Мы будем видеть в консоли flag value: true или flag value: false, что воспроизводит проблему

Первая вкладка

<body>
    <button id="btn">
    toogle flag
    </button>

    <script>
    const btn = document.getElementById('btn')

    btn.addEventListener('click', () => {
        setFlag(!getFlag())
    });
    setFlag(true)

    function setFlag(value) {
        window.localStorage.setItem('flag', value)
    }


    function getFlag() {
        return JSON.parse(window.localStorage.getItem('flag'))
    }
    </script>
</body>


Вторая вкладка

<body>
    <script>
    performAction()

    function setFlag(value) {
        window.localStorage.setItem('flag', value)
    }


    function getFlag() {
        return JSON.parse(window.localStorage.getItem('flag'))
    }


    function performAction() {
        const flagValue = getFlag()
        console.log('flag value: ', flagValue)
        if (flagValue) {
            console.log('flag value === true, set false then')
            setFlag(false)
        }
    }
    </script>
</body>


Возникает проблема безопасной записи и считывания данных в localStorage и мне интересны способы её решения.
  • Вопрос задан
  • 283 просмотра
Пригласить эксперта
Ответы на вопрос 2
@rPman
Увы, спецификация четко говорит:
4.5 Threads

Because of the use of the storage mutex, multiple browsing contexts will be able to access the local storage areas simultaneously in such a manner that scripts cannot detect any concurrent script execution.

Но, вы можете попытаться, и симулировать мютексы сами.

Классический пример, ищем атомарную операцию которая одновременно отметит факт блокировки в хранилище и выдаст ее состояние. Например реализация мютекс: lockState=++localStorage['lockState'];
Здесь вы получаете количество процессов, желающих получить доступ к объекту и одновременно повышаете количество блокировок, вы смело можете долго проверять это значение, и если оно больше 0 то не немного подождать и повторить попытку потом (само собой тут же уменьшив количество блокировок), когда именно, отдельный разговор, например можно завернуть работу с вашей базой через ваши методы, где и вызывать свои колбеки.
Ответ написан
Комментировать
zoonman
@zoonman
⋆⋆⋆⋆⋆
1. https://developer.mozilla.org/en-US/docs/Mozilla/A...

2. Вы можете общаться между вкладками посредством ServiceWorkers и window.postMessage.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы