首页 > 解决方案 > 如何在不覆盖其他写入操作的情况下将记录添加到 chrome.storage.local

问题描述

我正在创建一个 Chrome 扩展来测量某些类型的标签活动。因此,我通过 chrome.tabs 创建了各种事件侦听器,用于侦听 onReplaced、onUpdated、onCreated 和其他事件。

这些侦听器中的每一个都需要存储数据,我正在使用 Storage API (chrome.storage.local) 来做到这一点。然而,存储 API 是基于回调的,正如其规范中所定义的那样。

chrome.storage.local.get(key, myCallbackFunctionHere);

发生的情况是我在几乎同时触发的各种事件侦听器中对存储进行了多次调用。因此,您对存储 API 有两个不同get的调用,然后set调用存储一些数据。两组数据都需要存储,但它们会相互覆盖,最终只有一组数据被存储。

chrome.tabs.onCreated.addListener(onTabCreated);
chrome.tabs.onUpdated.addListener(onTabUpdated);

function onTabUpdated(tabId, changeInfo, tabAsUpdated) {
  var someRelevantData = // extract some data to be stored 
  chrome.storage.sync.get({'myKey' : {}}, function (data) { 
    var myDictionaryUpdated = data.myKey;
    myDictionaryUpdated ['newKey'] = someRelevantData;
    chrome.storage.sync.set({'myKey' : myDictionaryUpdated }, function() { 
      console.log("myKey updated --- new value is &s", JSON.stringify(myDictionaryUpdated, null, 4));
    });
  });
}

function onTabActivated(activeInfo) { 
  var someDifferentAndEquallyImportantData = // extract some data to be stored 
  chrome.storage.sync.get({'myKey' : {}}, function (data) { 
    var myDictionaryUpdated = data.myKey;
    myDictionaryUpdated['aSecondNewKey'] = someDifferentAndEquallyImportantData;
    chrome.storage.sync.set({'myKey' : myDictionaryUpdated }, function() { 
      console.log("myKey updated --- new value is &s", JSON.stringify(myDictionaryUpdated , null, 4));
    });
  });
}

onTabUpdated 的回调返回使用“myKey”存储的对象,然后添加到它并在存储中更新它。但是,onTabActivated 也会在 'myKey' 处获取对象(它仍然是空的,因为来自上一个回调的 set 调用尚未运行),添加自己的数据,然后将更新的对象写入存储,覆盖任何数据包含在 onTabUpdated 回调中。

我在其他任何地方都找不到存储 API 的良好文档或答案 - Stack Overflow 上的相关问题不涉及多个事件侦听器在彼此之后立即调用存储 API。

标签: javascriptgoogle-chrome-extensioncallbackevent-listenergoogle-chrome-storage

解决方案


这是任何异步存储的标准问题,并非特定于扩展。

解决方案 1. 使用同步window.localStorage. 它只能存储字符串,因此您需要 JSON.stringify 和 JSON.parse 来处理复杂数据。性能方面,假设数据小于几千字节,则没有区别,但如果有更多,则不要使用此方法。

解决方案 2. 使用等待上一次调用完成的互斥读/写函数:

const storage = (() => {
  let mutex = Promise.resolve();
  const API = chrome.storage.sync;
  const mutexExec = (method, data) => {
    mutex = Promise.resolve(mutex)
      .then(() => method(data))
      .then(result => {
        mutex = null;
        return result;
      });
    return mutex;
  };
  const syncGet = data => new Promise(resolve => API.get(data, resolve));
  const syncSet = data => new Promise(resolve => API.set(data, resolve));
  return {
    read: data => mutexExec(syncGet, data),
    write: data => mutexExec(syncSet, data),
  };
})();

用法:

async function example() {
  const {myKey} = await storage.read({myKey: {}}); 
  myKey.foo = 'bar';
  await storage.write({myKey});
}

解决方案 3:使用存储键值对的变量将缓存添加到以前的解决方案,但这是读者的练习(周围可能有现有的库/示例)。


推荐阅读