无功能地返回Chrome存储API值

Kyl*_*key 14 javascript function return-value google-chrome-extension google-chrome-storage

在过去的两天里,我一直在使用chrome异步存储.如果你有一个功能,它的工作"很好".(如下):

chrome.storage.sync.get({"disableautoplay": true}, function(e){
console.log(e.disableautoplay);
});
Run Code Online (Sandbox Code Playgroud)

我的问题是我无法使用我正在做的功能.我想把它归还,就像LocalStorage一样.就像是:

var a = chrome.storage.sync.get({"disableautoplay": true});
Run Code Online (Sandbox Code Playgroud)

要么

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

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

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

什么都行不通.它都返回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);
Run Code Online (Sandbox Code Playgroud)

必须有一些方法可以做一些事情

if(chrome.storage.sync.get("disableautoplay") == true);
Run Code Online (Sandbox Code Playgroud)

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

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

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.
Run Code Online (Sandbox Code Playgroud)

Xan*_*Xan 29

好的,很好,你可以得到你的问题的定制答案.它仍然是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();
Run Code Online (Sandbox Code Playgroud)

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

重要的是要理解(虽然在这种特殊情况下很明显)该函数不会在此时运行.它将只运行,当事件发生.一旦运行时确认'我将运行(或"回调",因此名称为"回调"),当发生这种情况时,执行就会继续执行.如果someMoreCode()试图访问clicks,它将是0,而不是1.

这就是所谓的异步性,因为这是在当前执行流程之外发生的事情.

第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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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


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

  • 需要运行时而不是JS来做某事(例如磁盘/网络访问)
  • 保证终止(无论是成功还是失败)

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

  • 假设在我们当前的连接上,运行时可以以100ms内可在JS中使用的形式请求,下载和处理文件.
  • 在另一个连接上,这更糟糕,它需要500毫秒.
  • 有时连接非常糟糕,因此运行时将等待1000毫秒并放弃超时.

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

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

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

var file = get("http://example.com/cat_video.mp4");
Run Code Online (Sandbox Code Playgroud)

但在一切都冻结的可怕,可怕的代价.事实上,现代浏览器认为这个已被弃用,这是一个非常糟糕的代价.以下是关于MDN主题讨论.


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

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

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

JS code <-> Runtime <-> Storage DB <-> Cache <-> File storage on disk
Run Code Online (Sandbox Code Playgroud)

这是一个复杂的事件链,需要暂停整个JS引擎.这导致了被认为是不可接受的性能.


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

因此,它包含了这样的范例:"我承认你的呼召,并且会回来结果,现在做一些有用的事情",如果在运行时做一些事情会有延迟.与XMLHttpRequest不同,这些调用没有同步版本.

引用文档:

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

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

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

假设您有以下同步代码:

var result = doSomething();
doSomethingElse(result);
Run Code Online (Sandbox Code Playgroud)

假设现在doSomething是异步的.然后这变成:

doSomething(function(result) {
  doSomethingElse(result);
});
Run Code Online (Sandbox Code Playgroud)

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

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

if (doABunchOfThings() == 42) {
  andNowForSomethingCompletelyDifferent()
}
Run Code Online (Sandbox Code Playgroud)

嗯..在这种情况下,您需要在回调中移动所有这些.return必须成为一个电话.

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

doABunchOfThings(function(result) {
  if (result == 42) {
    andNowForSomethingCompletelyDifferent();
  }
});
Run Code Online (Sandbox Code Playgroud)

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

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

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

TL部分; DR:我绝对必须让存储同步,停止!

有时候有合法的理由可以使用同步存储.例如,asyncAPI阻塞调用不能等待.或者回调地狱会让你付出沉重的代价.

你可以做的是拥有异步的同步缓存await.它带来了一些成本,但并非不可能.

考虑:

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
Run Code Online (Sandbox Code Playgroud)

如果你可以将所有初始化代码放在一个函数中webRequest,那么你有:

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

chrome.storage执行代码时,以及随后发生任何分配了处理程序的事件init(),init()将填充代码.你已经将异步性降低到了一个回调.

当然,这只是存储在执行时所看到的快照init().如果要保持存储的一致性,则需要设置storageCachevia get()事件的更新.由于JS的单事件循环特性,这意味着只有在代码不运行时才会更新缓存,但在许多情况下这是可以接受的.

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

实施这些是留下的练习.


小智 26

使主函数“异步”并在其中创建“承诺”:)

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);
}
Run Code Online (Sandbox Code Playgroud)


小智 6

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

let getFromStorage = keys => new Promise((resolve, reject) =>
  chrome.storage.sync.get(...keys, result => resolve(result)));
Run Code Online (Sandbox Code Playgroud)

  • 是的,这已经完成了一半,但它仍然使调用异步。您只能在“async”函数中“await”它。 (2认同)

Hai*_* Ai 5

  1. chrome.storage.sync.get没有返回值,这解释了为什么你undefined在调用类似的东西时会得到

    var a = chrome.storage.sync.get({"disableautoplay": true});
    
    Run Code Online (Sandbox Code Playgroud)
  2. chrome.storage.sync.get也是一个异步方法,这解释了为什么在下面的代码中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
    }
    
    Run Code Online (Sandbox Code Playgroud)