在过去的两天里,我一直在使用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;
});
我试过一百万种组合,甚至设置了一个公共(public)变量并设置为:
var a;
window.onload = function(){
chrome.storage.sync.get({"disableautoplay": true}, function(e){
a = e.disableautoplay;
});
}
没事。除非引用它的代码在get函数内部,否则所有返回的结果都是未定义的,这对我没有用。我只希望能够将值作为变量返回。
这有可能吗?
编辑:此问题不是重复的,请允许我解释原因:
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){
console.log(i.disableautoplay);
//But the info is worthless to me inside this function.
});
//I need it outside this function.
最佳答案
这是针对您问题的量身定制的答案。为何仍有90%的解释说明为什么您无法绕过异步,但请多多包涵-它将总体上为您提供帮助。我保证最后会有与chrome.storage
相关的东西。
在开始之前,我将重申以下规范链接:
(特定于Chrome,RobW的出色回答,可能最容易理解)
(关于异步JS的一个较老但同样受人尊敬的规范问题)
因此,让我们讨论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
)时,应调用处理程序函数。 重要的是要了解(尽管在这种情况下很明显)该功能目前尚未运行。该事件仅在以后发生时才运行。一旦运行时确认“在发生这种情况时,我将运行(或“回调”,因此名称为“callback”),执行将继续。”如果
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;
}
}
您可以单击一下内容,但是会增加
clicks
的代码正在耐心地等待(非终止)循环终止。哎呀。请注意,这段代码不仅会冻结这段代码:我们等待时不再处理每个事件,因为只有一个事件队列/线程。 JavaScript中只有一种方法可以让其他处理程序执行其工作:终止当前代码,并让运行时知道发生我们想要的操作时要调用的内容。
这就是为什么将异步处理应用于另一类调用的原因:
让我们来看一个经典的例子:AJAX调用。假设我们要从URL加载文件。
如果我们要等到此完成,就会有可变的,不可预测的且相对较长的延迟。由于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不同,这些调用没有同步版本。
引用文档:
第3节:如何接受异步性?
处理异步性的经典方法是回调链。
假设您具有以下同步代码:
var result = doSomething();
doSomethingElse(result);
假设现在
doSomething
是异步的。然后变成:doSomething(function(result) {
doSomethingElse(result);
});
但是,如果更复杂呢?说是:
function doABunchOfThings() {
var intermediate = doSomething();
return doSomethingElse(intermediate);
}
if (doABunchOfThings() == 42) {
andNowForSomethingCompletelyDifferent()
}
好吧..在这种情况下,您需要在回调中移动所有这些内容。
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。我不会在这里讨论它们(用完了空间),但是它们不会改 rebase 本的“此代码只会在以后运行”部分。
TL; DR节:我绝对必须使存储同步,暂停!
有时出于合理的原因拥有同步存储。例如,
async
API阻止调用无法等待。否则,回调 hell 将使您付出高昂的代价。您可以做的是拥有异步
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
如果您可以将所有初始化代码都放在一个函数
webRequest
中,那么您将拥有:var storageCache = {};
chrome.storage.sync.get(null, function(data) {
storageCache = data;
init(); // All your code is contained here, or executes later that this
});
通过执行
chrome.storage
中的时间代码,以及在之后,当在init()
中分配给处理程序的任何事件发生时,都将被填充。您已将异步性降低为ONE回调。当然,这只是执行
init()
时存储外观的快照。如果要保持存储的一致性,则需要通过 init()
/ storageCache
设置对get()
的更新。由于JS的单事件循环性质,这意味着仅在您的代码不运行时才更新缓存,但是在许多情况下这是可以接受的。同样,如果您想将对
storageCache
的更改传播到实际存储中,仅设置chrome.storage.onChanged
是不够的。您将需要编写storageCache
填充程序,然后将两者都写入storageCache['key']
并安排(异步)set(key, value)
。实现这些作为练习。