In den letzten zwei Tagen habe ich mit asynchronem Chrome-Speicher gearbeitet. Es funktioniert "gut", wenn Sie eine Funktion haben. (Wie unten):
%Vor%Mein Problem ist, dass ich mit dem, was ich mache, keine Funktion verwenden kann. Ich möchte es einfach zurückgeben, wie es LocalStorage kann. Etwas wie:
%Vor%oder
%Vor%Ich habe eine Million Kombinationen ausprobiert, sogar eine öffentliche Variable gesetzt und folgendes gesetzt:
%Vor%Nichts funktioniert. Alles kehrt undefiniert zurück, es sei denn, der Code, der darauf verweist, liegt innerhalb der Funktion des Get, und das ist für mich nutzlos. Ich möchte nur einen Wert als Variable zurückgeben können.
Ist das überhaupt möglich?
EDIT: Diese Frage ist kein Duplikat, bitte erlauben Sie mir zu erklären, warum:
1: Es gibt keine anderen Posts, die das spezifisch fragen (ich habe zwei Tage damit verbracht, zuerst zu suchen, nur für den Fall).
2: Meine Frage wird immer noch nicht beantwortet. Ja, Chrome Storage ist asynchron und gibt keinen Wert zurück. Das ist das Problem. Ich werde unten näher ausführen ...
Ich muss in der Lage sein, einen gespeicherten Wert außerhalb der Funktion chrome.storage.sync.get zu erhalten. Ich kann localStorage nicht verwenden, da es urlspezifisch ist und auf die gleichen Werte nicht über die browser_action-Seite der Chrome-Erweiterung und die background.js zugegriffen werden kann. Ich kann einen Wert nicht mit einem Skript speichern und mit einem anderen Skript darauf zugreifen. Sie werden separat behandelt.
Meine einzige Lösung ist die Verwendung von Chrome Storage. Es muss eine Möglichkeit geben, den Wert eines gespeicherten Elements zu erhalten und es außerhalb der get-Funktion zu referenzieren. Ich muss es in einer if-Anweisung überprüfen.
Genauso wie localStorage
machen kann %Vor%Es muss eine Möglichkeit geben, etwas nach dem Motto
zu tun %Vor%Mir ist klar, dass es nicht so einfach sein wird, aber das ist der beste Weg, es zu erklären.
Jeder Beitrag, den ich sehe, sagt es so:
%Vor% Okay, gut, Sie erhalten Ihre maßgeschneiderte Antwort auf Ihre Frage. Es wird immer noch eine 90% lange Erklärung sein, warum du nicht asynchron durchkommen kannst, aber ertrage mit mir - es wird dir im Allgemeinen helfen. Ich verspreche, dass am Ende% chrome.storage
etwas ist.
Das erste zu behandelnde Konzept ist Laufzeitumgebung . JavaScript ist in gewisser Weise in ein anderes Programm eingebettet, das seinen Ausführungsfluss steuert - in diesem Fall Chrome. Alle Ereignisse (Timer, Klicks usw.) stammen aus der Laufzeitumgebung. JavaScript-Code registriert Handler für Ereignisse, die von der Laufzeit gespeichert und entsprechend aufgerufen werden.
Zweitens ist es wichtig zu verstehen, dass JavaScript single-threaded ist. Es gibt eine einzelne Ereignisschleife , die von der Laufzeitumgebung verwaltet wird. Wenn beim Eintreten eines Ereignisses ein anderer Code ausgeführt wird, wird dieses Ereignis zur Verarbeitung in eine Warteschlange gestellt, wenn der aktuelle Code beendet wird .
Sehen Sie sich diesen Code an:
%Vor% Also, was passiert hier? Wenn dieser Code ausgeführt wird und folgende .addEventListener
erreicht wird, passiert folgendes: Die Laufzeitumgebung wird benachrichtigt, dass beim Auftreten des Ereignisses ( element
angeklickt) die Handler-Funktion aufgerufen wird.
Es ist wichtig zu verstehen (obwohl in diesem speziellen Fall es ziemlich offensichtlich ist), dass die Funktion nicht an diesem Punkt ausgeführt wird. Es wird später später ausgeführt, wenn dieses Ereignis eintritt. Die Ausführung wird fortgesetzt, sobald die Laufzeit quittiert 'Ich werde ausführen (oder "Rückruf", daher der Name "Rückruf") dies, wenn das passiert.' Wenn someMoreCode()
versucht, clicks
zuzugreifen Es wird 0
, nicht 1
sein.
Dies nennt man asynchronicity , da dies außerhalb des aktuellen Ausführungsflusses geschieht.
Nun, eine wichtige Überlegung. Angenommen, someMoreCode()
ist tatsächlich ein sehr lang laufender Code. Was passiert, wenn ein Klick während der Ausführung noch passiert?
JavaScript hat kein Interrupt-Konzept. Runtime sieht, dass Code ausgeführt wird, und bringt den Event-Handler-Aufruf in die Warteschlange. Der Handler wird erst ausgeführt, wenn someMoreCode()
vollständig abgeschlossen ist.
Obwohl ein Click-Ereignishandler in dem Sinne extrem ist, dass der Klick nicht garantiert ist, erklärt dies, warum Sie nicht auf das Ergebnis eines asynchronen Vorgangs warten können. Hier ist ein Beispiel, das nicht funktioniert:
%Vor% Sie können nach Herzenslust klicken, aber der Code, der clicks
inkrementiert, wartet geduldig darauf, dass die (nicht terminierende) Schleife beendet wird. Hoppla!
Beachten Sie, dass dieser Codeabschnitt nicht nur diesen Teil des Codes einfriert: Jedes einzelne Ereignis wird nicht mehr behandelt, während wir warten , da es nur eine Ereigniswarteschlange / einen Thread gibt. Es gibt nur eine Möglichkeit in JavaScript, anderen Handlern ihre Arbeit zu ermöglichen: beende den aktuellen Code und lasse die Runtime wissen, was zu tun ist, wenn etwas, was wir wollen, auftritt .
Dies ist der Grund, warum die asynchrone Behandlung auf eine andere Klasse von Aufrufen angewendet wird:
Kommen wir zu einem klassischen Beispiel: AJAX-Aufrufe. Angenommen, wir möchten eine Datei von einer URL laden.
Wenn wir warten würden, bis dies abgeschlossen ist, hätten wir eine variable, unvorhersehbare und relativ lange Verzögerung. Da JS-Warten funktioniert, würden alle anderen Handler (z. B. UI) ihre Arbeit für diese Verzögerung nicht ausführen, was zu einer eingefrorenen Seite führt.
Klingt vertraut? Ja, genau so funktioniert synchrone XMLHttpRequest . Statt einer while(1)
-Schleife im JS-Code passiert es im Wesentlichen im Laufzeitcode - da JavaScript anderen Code nicht ausführen lässt, während er wartet.
Ja, dies ermöglicht eine vertraute Form von Code:
%Vor%Aber zu einem schrecklichen, schrecklichen Preis für alles Gefrieren. Ein Preis, der so schrecklich ist, dass die modernen Browser dies als veraltet ansehen. Hier ist ein Diskussion zum Thema auf MDN .
Sehen wir uns nun localStorage
an. Es entspricht der Beschreibung von "Anruf zur Laufzeit beenden" und ist dennoch synchron. Warum?
Um es einfach zu sagen: historische Gründe (es ist eine sehr alte Spezifikation).
Obwohl es sicherlich vorhersagbarer ist als eine Netzwerkanforderung, benötigt localStorage
immer noch die folgende Kette:
Es ist eine komplexe Kette von Ereignissen, und die gesamte JS-Engine muss dafür angehalten werden. Dies führt zu was als inakzeptabel angesehen wird .
Jetzt sind Chrome-APIs von Grund auf auf Leistung ausgelegt. Sie können immer noch einige synchrone Aufrufe in älteren APIs wie chrome.extension
sehen, und es gibt Aufrufe, die in JS verarbeitet werden (und daher sinnvoll sind), aber chrome.storage
ist (relativ) neu / p>
Als solches umfasst es das Paradigma "Ich erkenne Ihren Anruf an und werde mit Ergebnissen zurückkommen, tue jetzt etwas Nützliches" wenn es zu Verzögerungen kommt, wenn etwas mit Runtime gemacht wird. Im Gegensatz zu XMLHttpRequest gibt es keine synchronen Versionen dieser Aufrufe.
Zitieren der Dokumente:
Es ist [
chrome.storage
] asynchron zu Massenlese- und Schreiboperationen und daher schneller als die blockierende und seriellelocalStorage
API.
Der klassische Weg, mit Asynchronität umzugehen, sind Callback-Ketten.
Angenommen, Sie haben den folgenden synchronen Code:
%Vor% Angenommen, doSomething
ist jetzt asynchron. Dann wird das:
Aber was ist, wenn es noch komplexer ist? Sag es war:
%Vor% Nun .. In diesem Fall müssen Sie all im Callback verschieben. return
muss stattdessen ein Aufruf werden.
Hier haben Sie eine Kette von Callbacks: doABunchOfThings
ruft doSomething
sofort auf, was terminiert, aber irgendwann später ruft doSomethingElse
auf, wobei das Ergebnis if
durch ein anderes ersetzt Rückruf.
Offensichtlich kann die Schichtung davon unordentlich werden. Niemand hat gesagt, dass JavaScript eine gute Sprache ist. Willkommen bei Callback Hell .
Es gibt Tools, um es besser zu verwalten, zum Beispiel Versprechen . Ich werde sie hier nicht besprechen (der Platz wird knapp), aber sie ändern nicht den grundlegenden "dieser Code wird nur später laufen" Teil.
Manchmal gibt es legitime Gründe für einen synchronen Speicher. Zum Beispiel können webRequest
API blockierende Aufrufe nicht warten. Oder Callback Hell wird dich teuer zu stehen kommen.
Was Sie tun können, ist ein synchroner Cache des asynchronen chrome.storage
. Es kommt mit einigen Kosten, aber es ist nicht unmöglich.
Überlegen Sie:
%Vor% Wenn Sie ALLEN Initialisierungscode in eine Funktion init()
setzen können, haben Sie folgendes:
Wenn der Zeitcode in init()
ausgeführt wird, und danach , wird init()
aufgefüllt, wenn ein Ereignis zugewiesen wurde, dem Handler in storageCache
zugeordnet wurden . Sie haben die Asynchronität auf ONE Callback reduziert.
Dies ist natürlich nur eine Momentaufnahme des Speicherplatzes, der zum Zeitpunkt der Ausführung von get()
angezeigt wird. Wenn Sie die Kohärenz mit dem Speicher aufrechterhalten möchten, müssen Sie Updates für storageCache
über chrome.storage.onChanged
events . Aufgrund der Single-Event-Loop-Eigenschaft von JS bedeutet dies, dass der Cache nur aktualisiert wird, wenn der Code nicht ausgeführt wird. In vielen Fällen ist dies jedoch akzeptabel.
Ebenso, wenn Sie Änderungen an storageCache
auf den realen Speicher propagieren möchten, reicht es nicht, nur storageCache['key']
zu setzen. Sie müssten ein set(key, value)
shim schreiben, das BEIDE in storageCache
schreibt und ein (asynchrones) chrome.storage.sync.set
plant.
Diese zu implementieren ist eine Übung.
chrome.storage.sync.get
hat keine zurückgegebenen Werte, weshalb Sie undefined
beim Aufruf erhalten würden etwas wie
chrome.storage.sync.get
ist auch ein asynchrone -Methode, was erklärt, warum im folgenden Code a
undefined
wäre, wenn Sie nicht innerhalb der Callback-Funktion darauf zugreifen.
Wenn du es schaffen würdest, das herauszufinden, hast du eine Quelle seltsamer Fehler gemacht. Nachrichten werden asynchron ausgeführt. Dies bedeutet, dass beim Senden einer Nachricht der Rest des Codes ausgeführt werden kann, bevor die asynchrone Funktion zurückkehrt. Dafür gibt es keine Garantie, da chrome multi-threaded ist und die get-Funktion sich verzögern kann, d. H. Hdd ist beschäftigt. Verwenden Sie Ihren Code als Beispiel:
%Vor%Es wird also besser sein, wenn Sie so etwas verwenden:
%Vor%Wenn Sie diesen Wert wirklich zurückgeben möchten, verwenden Sie die JavaScript-Speicher-API. Dadurch werden nur Zeichenfolgenwerte gespeichert, sodass Sie den Wert vor dem Speichern und nach dem Abrufen des Werts umsetzen müssen.
%Vor%Tags und Links javascript google-chrome-extension function return-value google-chrome-storage