Rückgabe des Chrome-Speicher-API-Werts ohne Funktion

8

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%     
Kyle Hickey 17.07.2016, 20:41
quelle

3 Antworten

12

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.

Bevor wir anfangen, werde ich dafür noch kanonische Links wiederholen:

Lassen Sie uns JS-Asynchronität diskutieren.

Abschnitt 1: Was ist das?

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.

Abschnitt 2: Warum wird es benötigt oder warum synchrone APIs aussterben?

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:

  • benötigt die Laufzeit und nicht JS, um etwas zu tun (zB Festplatten- / Netzwerkzugriff)
  • werden garantiert beendet (ob Erfolg oder Misserfolg)

Kommen wir zu einem klassischen Beispiel: AJAX-Aufrufe. Angenommen, wir möchten eine Datei von einer URL laden.

  • Nehmen wir an, die Laufzeitumgebung kann die Datei in unserer aktuellen Verbindung anfordern, herunterladen und verarbeiten, und zwar in der Form, die in JS in 100ms verwendet werden kann.
  • Bei einer anderen Verbindung ist das etwas schlechter, es würde 500ms dauern.
  • Und manchmal ist die Verbindung wirklich schlecht, also wartet Runtime auf 1000ms und gibt mit einem Timeout auf.

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:

%Vor%

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 serielle localStorage API.

Abschnitt 3: Wie kann Asynchronität berücksichtigt werden?

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:

%Vor%

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.

%Vor%

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.

Sektion TL; DR: Ich muss unbedingt den Speicher synchron haben, halp!

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:

%Vor%

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.

    
Xan 19.07.2016 10:49
quelle
1
  1. chrome.storage.sync.get hat keine zurückgegebenen Werte, weshalb Sie undefined beim Aufruf erhalten würden etwas wie

    %Vor%
  2. 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.

    %Vor%
Haibara Ai 18.07.2016 00:58
quelle
0

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%     
ze_iliasgr 18.07.2016 08:26
quelle