本文介绍了无功能地返回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){
return e.disableautoplay;
});

我试过一百万个组合,甚至设置一个公共变量并设置它:

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

没什么用。除非引用它的代码在get的函数内部,否则它将全部返回undefined,这对我没用。我只是想能够将一个值作为一个变量返回。



这甚至可能吗?

编辑:这个问题不是重复的,请允许我解释为什么:



1:没有其他帖子特意提出这个问题(我花了两天的时间看第一个,以防万一)。

:我的问题仍然没有回答。是的,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); 

我意识到这不会那么简单,但这是我能解释它的最好方法。 / p>

我看到的每篇文章都说这样做:

  chrome .storage.sync.get({disableautoplay:true,function(i){
console.log(i.disableautoplay);
//但是这个信息在这个函数里是毫无价值的。 b $ b});
//我在这个函数之外需要它。


解决方案

好的,很好,您可以为您量身定制答案题。它仍然是90%长的解释,为什么你无法绕过异步,但承受我 - 它会帮助你一般。我承诺最后有一些与 chrome.storage 有关的东西。

在我们开始之前,我会重申canonical links for this:






  • (Chrome特定的,RobW出色的答案,可能最容易理解

  • (您要求的常规规范参考) (关于JS异步性的电子书)



所以,让我们来讨论一下JS的异步性。



第1部分:它是什么?



第一个涵盖的概念是运行时环境。某种程度上,JavaScript嵌入到另一个控制其执行流程的程序中 - 在这种情况下,Chrome就是这样。发生的所有事件(定时器,点击等)来自运行时环境。 JavaScript代码注册事件的处理程序,这些事件会被运行时记住,并被适当地调用。

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



看看这段代码:

  var clicks = 0; 

someCode();
element.addEventListener(click,function(e){
console.log(哦,嘿,我被点击了!);
点击+ = 1;
});
someMoreCode();

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



理解这一点非常重要(尽管在这种情况下它相当明显)此时该函数不是运行的。当事件发生时,它只会在后面运行。如果 someMoreCode()会在运行时确认'我将运行(或回调,因此名称为回调), 会尝试访问点击,它会是 0 ,而不是 1 。



这就是所谓的异步性,因为这会在当前执行流程之外发生。第二部分:它为什么需要它,或者为什么同步API正在消失?

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



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

事件处理程序在点击不能保证发生的意义上是极端的,这解释了为什么你不能等待异步操作的结果。这是一个不起作用的例子:

  element.addEventListener(click,function(e){
console.log(哦,嘿,我点击!);
点击+ = 1;
});
while(1){
if(clicks> 0){
console.log(哦,嘿,我们点击确实!);
休息;


$ / code $ / pre

您可以点击您心中的内容,但是代码那会增加点击耐心地等待(非终止)循环终止。请注意,这段代码不仅会冻结这段代码:在我们等待的时候,每一个事件都不会被处理 ,因为只有一个事件队列/线程。 JavaScript中只有一种方法可以让其他处理程序完成他们的工作:终止当前代码,让运行时知道要发生什么事情时要调用什么






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


  • 要求运行时(而不是JS)执行某些操作(例如,磁盘/网络访问)

  • 保证终止(无论成功还是失败)


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




  • 假设在我们当前的连接上,运行时可以请求,下载和处理该文件的格式可以在JS中使用100毫秒。

  • 在另一个连接上,情况会更差,需要500毫秒。有时连接是非常糟糕的,所以运行时会等待1000毫秒,并放弃超时。



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



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



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

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

但是,一切都冻得可怕,可怕的代价。 实际上,现代浏览器认为这已被弃用,成本非常可怕。以下是。






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



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

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

  JS代码<  - >运行时间<  - >存储DB<  - >高速缓存<  - >磁盘上的文件存储

这是一个复杂的事件链,整个JS引擎需要暂停它。这导致。




现在,Chrome API已经从头开始为性能而设计。您仍然可以在较旧的 API中看到一些同步调用,例如 chrome.extension ,并且有一些调用是在JS中处理的同步),但 chrome.storage 是(相对)新的。 ,如果有延迟与运行时间一起做某事。这些调用没有同步版本,与XMLHttpRequest不同。



引用文档:



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



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



假设您有以下同步代码:

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

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

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

但如果它更复杂呢?假设它是:

$ p $ 函数doABunchOfThings(){
var intermediate = doSomething();
return doSomethingElse(intermediate);
}

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

好的。在这种情况下,您需要在回调中移动 all 。 return 必须成为调用。

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


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

这里有一个回调链: doABunchOfThings 立即调用 doSomething ,这会终止,但稍后调用 doSomethingElse ,如果通过另一个回调被提供给。



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



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



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



有时候有合理的理由需要同步存储。例如, webRequest API拦截调用不能等待。或者Callback Hell会让你付出沉重的代价。

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



考虑:

  var storageCache = {}; 
chrome.storage.sync.get(null,function(data){
storageCache = data;
//现在你有一个同步快照!
});
//不是这样,直到内部代码运行

如果可以的话把所有的初始化代码放在一个函数 init()中,然后你有这个:

  var storageCache = {}; 
chrome.storage.sync.get(null,function(data){
storageCache = data;
init(); //所有代码都包含在这里,或者稍后执行, b $ b});

通过 init()在 init()中发生的任何事件发生, storageCache 时执行 >将被填充。您已将异步性降低到ONE回调。



当然,这仅仅是执行 get()时存储的快照。 。如果您想保持与存储的一致性,则需要通过。由于JS的单一事件循环性质,这意味着缓存只会在代码不运行时更新,但在很多情况下这是可以接受的。



同样,如果您想将 storageCache 的更改传播到实际存储中,只需设置 storageCache ['key'] 为不够。您需要编写一个 set(key,value) shim,BOTH写入 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.
解决方案

Okay, fine, you get your 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 even 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:

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. 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值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-23 04:26