There is nothing that will set a value in all contexts which you don't have to obtain asynchronously. You're going to have to deal with it.
Primary method: Wait for the info. Delay execution until available.
The normal way to deal with this type of situation, is to just put all processing which might need the information into the program flow that begins with the callback of the the asynchronous call to storage.*
. This is normal and expected with asynchronous programming. In almost all cases, this is how you should deal with it.
Waiting for the info should be easy for scripts in the background context (the background script/event page, popups, options pages, etc.) For anything other than a background script, these scripts are almost all user-interaction based functionality, which means the delay for a fetch to storage.local
should be trivial. If it's not, you can always copy the value to a variable in the background page, which can be synchronously accessed directly by any script in the background context (see: Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)).
For the background script, you could also use localStorage
, which is synchronous. However, the data will not be available in all contexts.
For content scripts, needing to delay for the asynchronous data might mean moving your injection time earlier. In other words, you might need to inject (run_at
or runAt
) at document_start
instead of document_idle
or document_end
. You would then need to delay the rest of processing until after the document is ready/loaded.
For content scripts already loaded at document_start
Specifically for console.log()
For your specific issue, output via console.log()
, you can queue the output which would normally be sent to console.log()
until after you have the data back from the async call to storage.local
. Once you have the data, you can then either A) output everything in the queue and set everything to output normally, or B) dump everything in the queue and set every call to be dropped.
All of this can easily be done with just overwriting the function pointer at console.log
to point to the queue function, the normal console.log()
, or a null function. Thus, there would be no need to change how the rest of your script calls console.log()
. If you are using other functions in console
, it would get a bit more complicated, but console.log()
is easy (I've done it, for performance reasons during testing, not for this particular issue).
In general (i.e. for things which can not be queue'ed for later)
This one is at best, non-trivial. There's no good solution for scripts injected via a manifest.json content_scripts
entry.
Partition your code
Separate your code into what must be done immediately and what can wait for the callback. This may require you to manipulate the page such that some actions in the normal loading process are delayed until after you have the information. What you would actually need to do here will vary depending on exactly what you are wanting to accomplish. The best solution may end up being to have your code error on one side or the other of what your configuration would change, then patch things up to what they should be once you get the configuration information that indicates it should be the other way.
For example, an extension that is intended to prevent JavaScript from running in the page, but has a whitelist of permitted scripts/domains could just go ahead and remove all of the <script>
elements as they are inserted. Then, once the configuration information is available, the permitted <scripts>
could be re-inserted into the DOM.
Use chrome.tabs.executeScript()
With chrome.tabs.executeScript()
you can inject a constructed script with configuration information prior to the injection of your main script. These scripts are in the same context so the first script can set variable(s) containing config information. For more information see: Pass a parameter to a content script injected using chrome.tabs.executeScript()
The background script (which executes the chrome.tabs.executeScript()
) is in a different process than the process for the tab (the one that actually injects the script). Thus, when exactly a script is injected is going to be indeterminate due to the asynchronous nature of having multiple proceses. This means that you can not 100% guarantee that your script will be injected prior to the initial HTML being loaded into the DOM. You can usually inject prior to that, but not always.
When to inject for the earliest possible time is a bit complicated. One of the problems is that if you call tabs.executeScript()
too early in the process of loading a new page, the injection does not take place in the new page. My testing (which I need to go back an re-do as I believe it could be improved) indicated:
Related: Google chrome extension: how to inject script immediately after page reload?