无功能地返回Chrome存储API值

时间:2016-07-17 20:41:15

标签: javascript function google-chrome-extension return-value google-chrome-storage

过去两天我一直在使用chrome异步存储。它有效"罚款"如果你有一个功能。 (如下):

chrome.storage.sync.get({"disableautoplay": true}, function(e){
console.log(e.disableautoplay);
});

我的问题是我无法使用我正在做的功能。我想像LocalStorage一样返回它。类似的东西:

var a = chrome.storage.sync.get({"disableautoplay": true});

var a = chrome.storage.sync.get({"disableautoplay": true}, function(e){
    return e.disableautoplay;
});

我尝试了一百万种组合,甚至设置一个公共变量并设置:

var a;
window.onload = function(){
    chrome.storage.sync.get({"disableautoplay": true}, function(e){
    a = e.disableautoplay;
});
}

没有任何作用。它都返回undefined,除非引用它的代码在get函数内部,这对我没用。我只是希望能够将值作为变量返回。

这甚至可能吗?

编辑:这个问题不重复,请允许我解释原因:

1:没有其他帖子专门询问此事(我花了两天时间先查看,以防万一)。

2:我的问题仍未得到解答。是的,Chrome Storage是异步的,是的,它不会返回值。这就是问题所在。我将在下面详细说明......

我需要能够在chrome.storage.sync.get函数之外获取存储值。我无法使用localStorage,因为它是特定于URL的,并且无法从chrome扩展的browser_action页面和background.js访问相同的值。我无法使用一个脚本存储值并使用另一个脚本访问它。他们分开处理。

所以我唯一的解决方案是使用Chrome存储。必须有某种方法来获取存储项的值并在get函数之外引用它。我需要在if语句中检查它。

就像localStorage可以做的那样

if(localStorage.getItem("disableautoplay") == true);

必须有一些方法可以按照

的方式做点什么
if(chrome.storage.sync.get("disableautoplay") == true);

我意识到这不会那么简单,但这是我解释它的最佳方式。

我看到的每个帖子都是这样说的:

chrome.storage.sync.get({"disableautoplay": true, function(i){
    console.log(i.disableautoplay);
    //But the info is worthless to me inside this function.
});
//I need it outside this function.

5 个答案:

答案 0 :(得分:23)

好的,您可以根据自己的问题获得量身定制的答案。它仍然是90%的长解释为什么你不能解决异步,但请耐心等待 - 它会帮助你。我保证最后会有chrome.storage的相关内容。

在我们开始之前,我将重申规范链接:

所以,让我们讨论JS异步性。

第1节:它是什么?

要涵盖的第一个概念是运行时环境。在某种程度上,JavaScript嵌入在另一个控制其执行流程的程序中 - 在本例中为Chrome。发生的所有事件(计时器,点击等)都来自运行时环境。 JavaScript代码注册事件的处理程序,这些事件由运行时记住,并在适当时调用。

其次,理解JavaScript是单线程的,这一点很重要。运行时环境维护一个事件循环;如果在事件发生时执行了其他一些代码,那么该事件将被放入队列中以便在当前代码终止时进行处理

看看这段代码:

var clicks = 0;

someCode();
element.addEventListener("click", function(e) {
  console.log("Oh hey, I'm clicked!");
  clicks += 1;
});
someMoreCode();

那么,这里发生了什么?当执行此代码时,当执行到达.addEventListener时,会发生以下情况:通知运行时环境,当事件发生时(单击element),它应该调用处理函数。 /强>

理解这个功能在这一点上运行是很重要的(虽然在这个特殊情况下很明显)。当事件发生时,它只会在以后运行。一旦运行时确认'我将运行(或"回调"因此名称"回调"),当发生这种情况时,执行将继续执行。&# 39; 如果someMoreCode()尝试访问clicks,则会0,而不是1

这就是所谓的 asynchronicity ,因为这是在当前执行流程之外发生的事情。

第2节:为什么需要它,或者为什么同步API会消失?

现在,一个重要的考虑因素。假设someMoreCode()实际上是一段非常长时间运行的代码。如果点击事件仍在运行时会发生什么?

JavaScript没有中断的概念。运行时将看到代码正在执行,并将事件处理程序调用放入队列中。 处理程序在someMoreCode()完成之前不会执行。

虽然点击事件处理程序在某种意义上是极端的,因为无法保证点击发生,但这解释了为什么你不能等待异步操作的结果。这是一个不会工作的例子:

element.addEventListener("click", function(e) {
  console.log("Oh hey, I'm clicked!");
  clicks += 1;
});
while(1) {
  if(clicks > 0) {
    console.log("Oh, hey, we clicked indeed!");
    break;
  }
}

您可以点击您心中的内容,但会增加clicks的代码会耐心地等待(非终止)循环终止。糟糕。

请注意,这段代码不会冻结这段代码:我们等待时不再处理每个事件,因为只有一个事件队列/线程。 JavaScript中只有一种方法可以让其他处理程序完成它们的工作:终止当前代码,让运行时知道在我们想要的东西发生时调用什么

这就是异步处理应用于另一类调用的原因:

  • 要求运行时而不是JS执行某些操作(例如磁盘/网络访问)
  • 保证终止(无论是成功还是失败)

让我们来看一个经典的例子:AJAX调用。假设我们要从URL加载文件。

  • 让我们说在我们当前的连接上,运行时可以以100ms内可以在JS中使用的形式请求,下载和处理文件。
  • 另一方面,那更糟糕,需要500毫秒。
  • 有时连接非常糟糕,因此运行时间会等待1000毫秒并放弃超时。

如果我们要等到这个完成,我们会有一个变量,不可预测和相对较长的延迟。由于JS等待的工作原理,所有其他处理程序(例如UI)都不会为此延迟做好工作,导致冻结页面。

听起来很熟悉?是的,这正是同步XMLHttpRequest的工作方式。而不是JS代码中的while(1)循环,它实际上发生在运行时代码中 - 因为JavaScript在等待时不能让其他代码执行。

是的,这允许使用熟悉的代码形式:

var file = get("http://example.com/cat_video.mp4");

但是,一切都冻结的可怕,可怕的代价。 成本非常糟糕,事实上,现代浏览器认为这已被弃用。这里是discussion on the topic on MDN

现在让我们看一下localStorage。它匹配"终止对运行时"的调用的描述,但它是同步的。为什么呢?

简单地说:历史原因(这是一个非常古老的规范)。

虽然它比网络请求更可预测,但localStorage仍然需要以下链:

JS code <-> Runtime <-> Storage DB <-> Cache <-> File storage on disk

这是一个复杂的事件链,需要暂停整个JS引擎。这会导致what is considered unacceptable performance

现在,Chrome API从一开始就是为性能而设计的。您仍然可以在较早的 API(例如chrome.extension)中看到一些同步调用,并且有些调用是在JS中处理的(因此在同步时有意义),但chrome.storage是(相对的) )新的。

因此,embraces the paradigm "I acknowledge your call and will be back with results, now do something useful meanwhile"如果在运行时做某事需要延迟。与XMLHttpRequest不同,这些调用没有同步版本。

引用文档:

  

它[chrome.storage]与批量读写操作异步,因此比阻塞和串行 localStorage API更快。

第3节:如何拥抱异步性?

处理异步性的经典方法是回调链。

假设您有以下同步代码:

var result = doSomething();
doSomethingElse(result);

假设现在doSomething是异步的。然后这就变成了:

doSomething(function(result) {
  doSomethingElse(result);
});

但是,如果它更复杂呢?说它是:

function doABunchOfThings() {
  var intermediate = doSomething();
  return doSomethingElse(intermediate);
}

if (doABunchOfThings() == 42) {
  andNowForSomethingCompletelyDifferent()
}

嗯..在这种情况下,您需要在回调中移动 all return必须成为电话。

function doABunchOfThings(callback) {
  doSomething(function(intermediate) {
    callback(doSomethingElse(intermediate));
  });
}

doABunchOfThings(function(result) {
  if (result == 42) {
    andNowForSomethingCompletelyDifferent();
  }
});

这里有一系列回调:doABunchOfThings立即调用doSomething,终止,但稍后调用doSomethingElse,其结果是通过另一个回调来if

显然,这种分层可能会变得混乱。 嗯,没有人说JavaScript是一种很好的语言..欢迎来到Callback Hell

有一些工具可以让它更易于管理,例如Promises。我不会在这里讨论它们(用完空间),但是它们并没有改变基础&#34;这段代码只能在以后运行&#34;一部分。

Section TL; DR:我绝对必须拥有存储同步,halp!

有时存在同步存储的合理原因。例如,webRequest API阻止调用无法等待。或者回调地狱会让你付出沉重的代价。

您可以做的是拥有异步chrome.storage的同步缓存。它带来了一些成本,但这并非不可能。

考虑:

var storageCache = {};
chrome.storage.sync.get(null, function(data) {
  storageCache = data;
  // Now you have a synchronous snapshot!
});
// Not HERE, though, not until "inner" code runs

如果您可以将所有初始化代码放在一个函数init()中,那么您有:

var storageCache = {};
chrome.storage.sync.get(null, function(data) {
  storageCache = data;
  init(); // All your code is contained here, or executes later that this
});

根据init()中的时间代码执行,之后init()中分配了处理程序的任何事件发生时,将填充storageCache。你已经将异步性降低到了一个回调。

当然,这只是存储在执行get()时所看到的内容的快照。如果您希望与存储保持一致,则需要通过chrome.storage.onChanged events设置storageCache的更新。由于JS的单事件循环特性,这意味着缓存只会在代码运行时更新,但在许多情况下可以接受。

同样,如果您想将更改传播到storageCache到真实存储,那么仅设置storageCache['key']是不够的。您需要编写一个set(key, value)填充程序,BOTH写入storageCache并调度(异步)chrome.storage.sync.set

实施这些是留下的练习。

答案 1 :(得分:2)

  1. chrome.storage.sync.get没有返回值,这解释了为什么在调用

    之类的内容时会得到undefined
    var a = chrome.storage.sync.get({"disableautoplay": true});
    
  2. chrome.storage.sync.get也是asynchronous方法,它解释了为什么以下代码a将是undefined,除非您在回调函数中访问它。

    var a;
    window.onload = function(){
        chrome.storage.sync.get({"disableautoplay": true}, function(e){
            // #2
            a = e.disableautoplay; // true or false
        });
        // #1
        a; // undefined
    }
    

答案 2 :(得分:2)

使主要功能“异步”并在其中进行“承诺”:)

async function mainFuction() {
    var p = new Promise(function(resolve, reject){
        chrome.storage.sync.get({"disableautoplay": true}, function(options){
            resolve(options.disableautoplay);
        })
    });

    const configOut = await p;
    console.log(configOut);
}

答案 3 :(得分:0)

如果你能设法解决这个问题,你就会产生一些奇怪的错误。消息以异步方式执行,这意味着当您发送消息时,其余代码可以在异步函数返回之前执行。由于chrome是多线程的并且get函数可能会延迟,即hdd很忙,因此无法保证这一点。 以您的代码为例:

var a;
window.onload = function(){
    chrome.storage.sync.get({"disableautoplay": true}, function(e){
    a = e.disableautoplay;
});
}

if(a)
   console.log("true!");
else
   console.log("false! Maybe undefined as well. Strange if you know that a is true, right?");

所以如果你使用这样的东西会更好:

chrome.storage.sync.get({"disableautoplay": true}, function(e){
    a = e.disableautoplay;
    if(a)
      console.log("true!");
    else
      console.log("false! But maybe undefined as well");
});

如果您确实想要返回此值,请使用javascript存储API。这只存储字符串值,因此您必须在存储之前和获取之后转换值。

//Setting the value
localStorage.setItem('disableautoplay', JSON.stringify(true));

//Getting the value
var a = JSON.stringify(localStorage.getItem('disableautoplay'));

答案 4 :(得分:0)

是的,您可以使用promise来实现:

let getFromStorage = keys => new Promise((resolve, reject) =>
  chrome.storage.sync.get(...keys, result => resolve(result)));