LocalStorage 在警报块期间未更新

LocalStorage not updated during alert-block

今天我遇到了本地存储的奇怪行为,我将其分解为一个简单的场景。如果在写入值期间出现警告框,Chrome 浏览器似乎不会同步 localStorage

打开了 2 个浏览器选项卡。

选项卡 A 请求文件 read.html:

    var item = localStorage.getItem('test');
    console.log('item before alert', item);

    alert('Pause!'); // don't close it before calling write.html

    item = localStorage.getItem('test');
    console.log('item after alert', item);

显示"Pause!" 的警告框不会关闭。

另一个选项卡 B 正在请求文件 write.html:

    var item = (localStorage.getItem('test') || '') + 'a';
    console.log('new item:', item);
    localStorage.setItem('test', item);

您可以在浏览器 (Chrome) Devtools 的 localStorage 中看到该值已更新,但仅在 Tab B 之一中。

这只发生在 Chrome。 IE11 和 Firefox 按预期工作。有谁知道这是设计使然还是 Chrome 中的错误?

问候 马库斯

编辑:使用以下代码创建了一个 github 项目:https://github.com/wondee/localStorage-bug

更新:在 Artyoms 回答后,我试图等待调度,这确实有助于在 read.html 中添加以下代码,最后 console.log(..) 更改正确反映:

setTimeout(() => console.log('item after 1s:', localStorage.getItem('test')), 1);

您是否尝试过在设置新值之前清除 localStorage?尝试

localStorage.removeItem('test');
localStorage.setItem('test',item);

根据spec(阅读到最后以涵盖可能的问题)。

When the setItem(), removeItem(), and clear() methods are called on a Storage object x that is associated with a local storage area, if the methods did not throw an exception or "do nothing" as defined above, then for every Document object whose relevant global object's localStorage attribute's Storage object is associated with the same storage area, other than x, send a storage notification.

基本上,据我了解,当用户打开具有相同来源的新选项卡时,会创建存储副本。因此,当在一个选项卡中修改存储时 - 将存储事件(任务排队)发送到所有其他选项卡(对于同一来源)以同步它们。

When a user agent is to send a storage notification for a Document, the user agent must queue a task to fire an event named storage at the Document object's relevant global object, using StorageEvent.

但在您的特定情况下,由于同步 alert(),在您的脚本完成且调用堆栈为空之前,事件循环无法获取此排队的存储事件。

但是一旦第一个选项卡中的脚本完成,事件循环就会选择此事件并将更改反映在其存储副本上。

但是规范中有一个明确的警告,您应该避免像在您的示例中那样更新相同的共享状态,因为没有锁定机制。

Warning! The localStorage attribute provides access to shared state. This specification does not define the interaction with other browsing contexts in a multiprocess user agent, and authors are encouraged to assume that there is no locking mechanism. A site could, for instance, try to read the value of a key, increment its value, then write it back out, using the new value as a unique identifier for the session; if the site does this twice in two different browser windows at the same time, it might end up using the same "unique" identifier for both sessions, with potentially disastrous effects.