返回没有功能的 Chrome 存储 API 值 [英] Returning Chrome storage API value without function

查看:23
本文介绍了返回没有功能的 Chrome 存储 API 值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

过去两天我一直在使用 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){返回 e.disableautoplay;});

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

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

没有任何效果.除非引用它的代码在 get 的函数内,否则它都返回 undefined,这对我来说没用.我只想能够将值作为变量返回.

这甚至可能吗?

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

1:没有其他帖子专门问这个问题(我花了两天时间先看看,以防万一).

2:我的问题还没有得到解答.是的,Chrome 存储是异步的,是的,它不返回值.那就是问题所在.我会在下面详细说明...

我需要能够在 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){控制台日志(i.disableautoplay);//但是这个函数里面的信息对我来说毫无价值.});//我在这个函数之外需要它.

解决方案

这是针对您的问题量身定制的答案.为什么你不能绕过异步,它仍然会是 90% 长的解释,但请耐心等待 - 它通常会对你有所帮助.我保证最后会有一些与 chrome.storage 相关的东西.

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

那么,让我们来讨论一下 JS 异步性.

第 1 部分:它是什么?

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

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

看看这段代码:

var clicks = 0;一些代码();element.addEventListener("点击", function(e) {console.log("哦,嘿,我被点击了!");点击 += 1;});someMoreCode();

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

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

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

第 2 部分:为什么需要它,或者为什么同步 API 正在消失?

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

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

虽然从不能保证点击的意义上来说,点击事件处理程序是极端的,但这解释了为什么不能等待异步操作的结果.这是一个不起作用的例子:

element.addEventListener("click", function(e) {console.log("哦,嘿,我被点击了!");点击 += 1;});而(1){如果(点击> 0){console.log("哦,嘿,我们确实点击了!");休息;}}

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

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

<小时>

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

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

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

  • 假设在我们当前的连接上,运行时可以在 100 毫秒内以 JS 可以使用的形式请求、下载和处理文件.
  • 在另一个连接上,情况更糟,需要 500 毫秒.
  • 有时连接真的很糟糕,所以运行时会等待 1000 毫秒并超时放弃.

如果我们等到这一切完成,我们就会有一个可变的、不可预测的、相对较长的延迟.由于 JS 等待的工作方式,所有其他处理程序(例如 UI)不会在此延迟期间完成其工作,从而导致页面冻结.

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

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

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

但是一切都冻结了,代价是可怕的、可怕的.成本太可怕了,事实上,现代浏览器认为这已经过时了.这是一个关于 MDN 主题的讨论.

<小时>

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

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

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

JS 代码 <->运行时<->存储数据库 <->缓存 <->磁盘上的文件存储

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

<小时>

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

因此,它拥抱范式我承认您的电话,并将返回结果,现在同时做一些有用的事情",如果在运行时做一些事情会造成延迟.与 XMLHttpRequest 不同,这些调用没有同步版本.

引用文档:

<块引用>

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

第 3 部分:如何接受异步性?

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

假设您有以下同步代码:

var 结果 = doSomething();doSomethingElse(结果);

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

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

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

function doABunchOfThings() {var 中间 = doSomething();返回 doSomethingElse(中级);}如果(doABunchOfThings()== 42){andNowForSomethingCompletelyDifferent()}

好吧..在这种情况下,您需要在回调中移动all.return 必须改为调用.

function doABunchOfThings(callback) {doSomething(功能(中级){回调(doSomethingElse(中级));});}doABunchOfThings(函数(结果){如果(结果 == 42){andNowForSomethingCompletelyDifferent();}});

这里有一个回调链:doABunchOfThings 立即调用 doSomething,它会终止,但 稍后 调用 doSomethingElse,其结果通过另一个回调提供给 if.

显然,这种分层会变得混乱.好吧,没有人说 JavaScript 是一门好语言.. 欢迎来到回调地狱.

有一些工具可以使其更易于管理,例如 Promisesasync/await.我不会在这里讨论它们(空间不足),但它们不会改变此代码只会稍后运行"的基本部分.

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

有时使用同步存储是有正当理由的.例如,webRequest API 阻塞调用不能等待.或者回调地狱会让你付出惨痛的代价.

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

考虑:

var storageCache = {};chrome.storage.sync.get(空,功能(数据){存储缓存 = 数据;//现在你有了一个同步快照!});//不在这里,但直到内部"代码运行

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

var storageCache = {};chrome.storage.sync.get(空,功能(数据){存储缓存 = 数据;在里面();//你的所有代码都包含在这里,或者稍后执行});

init() 中的代码执行时,然后当在 init() 中分配处理程序的任何事件发生时storageCache 将被填充.您已将异步性降低到 ONE 回调.

当然,这只是在执行 get() 时存储看起来的快照.如果您想保持与存储的一致性,您需要通过 storageCache 的更新"noreferrer">chrome.storage.onChanged 事件.由于 JS 的单事件循环特性,这意味着缓存只会在您的代码未运行时更新,但在许多情况下这是可以接受的.

同样,如果要将storageCache 的更改传播到实际存储,仅设置storageCache['key'] 是不够的.您需要编写一个 set(key, value) 垫片,该垫片都写入 storageCache 并安排(异步)chrome.storage.sync.set.

实施这些留作练习.

For the past two days I have been working with chrome asynchronous storage. It works "fine" if you have a function. (Like Below):

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

My problem is that I can't use a function with what I'm doing. I want to just return it, like LocalStorage can. Something like:

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

or

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

I've tried a million combinations, even setting a public variable and setting that:

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

Nothing works. It all returns undefined unless the code referencing it is inside the function of the get, and that's useless to me. I just want to be able to return a value as a variable.

Is this even possible?

EDIT: This question is not a duplicate, please allow me to explain why:

1: There are no other posts asking this specifically (I spent two days looking first, just in case).

2: My question is still not answered. Yes, Chrome Storage is asynchronous, and yes, it does not return a value. That's the problem. I'll elaborate below...

I need to be able to get a stored value outside of the chrome.storage.sync.get function. I -cannot- use localStorage, as it is url specific, and the same values cannot be accessed from both the browser_action page of the chrome extension, and the background.js. I cannot store a value with one script and access it with another. They're treated separately.

So my only solution is to use Chrome Storage. There must be some way to get the value of a stored item and reference it outside the get function. I need to check it in an if statement.

Just like how localStorage can do

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

There has to be some way to do something along the lines of

if(chrome.storage.sync.get("disableautoplay") == true);

I realize it's not going to be THAT simple, but that's the best way I can explain it.

Every post I see says to do it this way:

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.

解决方案

Here's a tailored answer to your question. It will still be 90% long explanation why you can't get around async, but bear with me — it will help you in general. I promise there is something pertinent to chrome.storage in the end.

Before we even begin, I will reiterate canonical links for this:

So, let's discuss JS asynchonicity.

Section 1: What is it?

First concept to cover is runtime environment. JavaScript is, in a way, embedded in another program that controls its execution flow - in this case, Chrome. All events that happen (timers, clicks, etc.) come from the runtime environment. JavaScript code registers handlers for events, which are remembered by the runtime and are called as appropriate.

Second, it's important to understand that JavaScript is single-threaded. There is a single event loop maintained by the runtime environment; if there is some other code executing when an event happens, that event is put into a queue to be processed when the current code terminates.

Take a look at this code:

var clicks = 0;

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

So, what is happening here? As this code executes, when the execution reaches .addEventListener, the following happens: the runtime environment is notified that when the event happens (element is clicked), it should call the handler function.

It's important to understand (though in this particular case it's fairly obvious) that the function is not run at this point. It will only run later, when that event happens. The execution continues as soon as the runtime acknowledges 'I will run (or "call back", hence the name "callback") this when that happens.' If someMoreCode() tries to access clicks, it will be 0, not 1.

This is what called asynchronicity, as this is something that will happen outside the current execution flow.

Section 2: Why is it needed, or why synchronous APIs are dying out?

Now, an important consideration. Suppose that someMoreCode() is actually a very long-running piece of code. What will happen if a click event happened while it's still running?

JavaScript has no concept of interrupts. Runtime will see that there is code executing, and will put the event handler call into the queue. The handler will not execute before someMoreCode() finishes completely.

While a click event handler is extreme in the sense that the click is not guaranteed to occur, this explains why you cannot wait for the result of an asynchronous operation. Here's an example that won't work:

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;
  }
}

You can click to your heart's content, but the code that would increment clicks is patiently waiting for the (non-terminating) loop to terminate. Oops.

Note that this piece of code doesn't only freeze this piece of code: every single event is no longer handled while we wait, because there is only one event queue / thread. There is only one way in JavaScript to let other handlers do their job: terminate current code, and let the runtime know what to call when something we want occurs.


This is why asynchronous treatment is applied to another class of calls that:

  • require the runtime, and not JS, to do something (disk/network access for example)
  • are guaranteed to terminate (whether in success or failure)

Let's go with a classic example: AJAX calls. Suppose we want to load a file from a URL.

  • Let's say that on our current connection, the runtime can request, download, and process the file in the form that can be used in JS in 100ms.
  • On another connection, that's kinda worse, it would take 500ms.
  • And sometimes the connection is really bad, so runtime will wait for 1000ms and give up with a timeout.

If we were to wait until this completes, we would have a variable, unpredictable, and relatively long delay. Because of how JS waiting works, all other handlers (e.g. UI) would not do their job for this delay, leading to a frozen page.

Sounds familiar? Yes, that's exactly how synchronous XMLHttpRequest works. Instead of a while(1) loop in JS code, it essentially happens in the runtime code - since JavaScript cannot let other code execute while it's waiting.

Yes, this allows for a familiar form of code:

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

But at a terrible, terrible cost of everything freezing. A cost so terrible that, in fact, the modern browsers consider this deprecated. Here's a discussion on the topic on MDN.


Now let's look at localStorage. It matches the description of "terminating call to the runtime", and yet it is synchronous. Why?

To put it simply: historical reasons (it's a very old specification).

While it's certainly more predictable than a network request, localStorage still needs the following chain:

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

It's a complex chain of events, and the whole JS engine needs to be paused for it. This leads to what is considered unacceptable performance.


Now, Chrome APIs are, from ground up, designed for performance. You can still see some synchronous calls in older APIs like chrome.extension, and there are calls that are handled in JS (and therefore make sense as synchronous) but chrome.storage is (relatively) new.

As such, it embraces the paradigm "I acknowledge your call and will be back with results, now do something useful meanwhile" if there's a delay involved with doing something with runtime. There are no synchronous versions of those calls, unlike XMLHttpRequest.

Quoting the docs:

It's [chrome.storage] asynchronous with bulk read and write operations, and therefore faster than the blocking and serial localStorage API.

Section 3: How to embrace asynchronicity?

The classic way to deal with asynchronicity are callback chains.

Suppose you have the following synchronous code:

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

Suppose that, now, doSomething is asynchronous. Then this becomes:

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

But what if it's even more complex? Say it was:

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

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

Well.. In this case you need to move all this in the callback. return must become a call instead.

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

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

Here you have a chain of callbacks: doABunchOfThings calls doSomething immediately, which terminates, but sometime later calls doSomethingElse, the result of which is fed to if through another callback.

Obviously, the layering of this can get messy. Well, nobody said that JavaScript is a good language.. Welcome to Callback Hell.

There are tools to make it more manageable, for example Promises and async/await. I will not discuss them here (running out of space), but they do not change the fundamental "this code will only run later" part.

Section TL;DR: I absolutely must have the storage synchronous, halp!

Sometimes there are legitimate reasons to have a synchronous storage. For instance, webRequest API blocking calls can't wait. Or Callback Hell is going to cost you dearly.

What you can do is have a synchronous cache of the asynchronous chrome.storage. It comes with some costs, but it's not impossible.

Consider:

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

If you can put ALL your initialization code in one function init(), then you have this:

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

By the time code in init() executes, and afterwards when any event that was assigned handlers in init() happens, storageCache will be populated. You have reduced the asynchronicity to ONE callback.

Of course, this is only a snapshot of what storage looks at the time of executing get(). If you want to maintain coherency with storage, you need to set up updates to storageCache via chrome.storage.onChanged events. Because of the single-event-loop nature of JS, this means the cache will only be updated while your code doesn't run, but in many cases that's acceptable.

Similarly, if you want to propagate changes to storageCache to the real storage, just setting storageCache['key'] is not enough. You would need to write a set(key, value) shim that BOTH writes to storageCache and schedules an (asynchronous) chrome.storage.sync.set.

Implementing those is left as an exercise.

这篇关于返回没有功能的 Chrome 存储 API 值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆