Jump to content

Chrome Extension: Pause script until all messages have been passed


Myth of Echelon

Recommended Posts

Hey guys, long time no see.

 

I have an issue with a Chrome extension I'm developing, that I've had for weeks and no one has been able to answer yet.

 

The Question:

When passing messages between a Google Chrome extension's background page and content script, respectively, is there any way to make it asynchronous - that is, to delay/pause the JavaScript until all of the messages are detected as being successfully being passed?

 

The Explanation:

I have a function immediately after the message-passing function that makes use of the

localStorage

data that is passed. On first runs the script always results in an error, due to the data not being passed (and therefore cached) fast enough.

 

Currently, I'm circumventing this with

setTimeout(nextFunction, 250);

but that's hardly an elegant or practical solution, as the amount and size of the values passed is always going to change and I have no way of knowing how long it needs to pass the values. Plus, I would imagine, that passing times are relative to the browser version and the user's system.

 

In short, I need it to be dynamic.

 

I have considered something like this:

function passMessages(){
	chrome.extension.sendRequest({method: "methodName"}, function(response) {
		localStorage["lsName"] = response.data;
	});

	checkPassedMessages();
}

function checkPassedMessages(){
	if (!localStorage["lsName"]){
		setTimeout(checkPassedMessages, 100); //Recheck until data exists
	} else {
		 continueOn();
	}
}

but I need to pass quite a lot of data (at least 20 values) and, frankly, that "solution" just isn't practical, due to the amount of IF conditions that it would require. Plus I don't even know if that would work or not.

 

Does anyone have any ideas?

 

Thanks.

Edited by Myth of Echelon
Link to comment
Share on other sites

Could you post the both the content JavaScript and the extension JavaScipt?

 

I have not worked with chrome extensions, but they look like regular js (wrapped inside a custom google framework)?

Link to comment
Share on other sites

Yes, it is just standard JavaScript wrapped in Chrome's framework. But my question is actually about Google's framework, so I don't know how far we'll get, as you said you have no personal experience.

 

I'll post you the basic template of message passing from background to content. There's nothing wrong with the functionality, so there's no reason to include irrelevant bits.

 

background.js

function addMessageListeners(){
 chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
  if (request.method == "methodName1"){
	sendResponse({data: localStorage["lsName1"]}); //Send the localStorage data inside "response" if the method name is "methodName1"
  }

  if (request.method == "methodName2"){
	sendResponse({data: localStorage["lsName2"]});
  }

  //etc
}
 );
}

 

content.js

function passMessages(){
 chrome.extension.sendRequest({method: "methodName1"}, function(response) {
localStorage["lsName1"] = response.data; //Send message request with method name "methodName1". When a response is received enter the "response" data back into localStorage
 });

 chrome.extension.sendRequest({method: "methodName2"}, function(response) {
localStorage["lsName2"] = response.data;
 });
}

 

(localStorage data is not accessible from within the content script unless passed through in a message)

Edited by Myth of Echelon
Link to comment
Share on other sites

Ok, I sort of understand,

 

Does each method name map to some sort of Key/Value? if so (and this is off the top of my head):

 


function addMessageListeners(){
 chrome.extension.onRequest.addListener(
	function(request, sender, sendResponse) {

	 var map = {methodName1: "lsName1", methodName2: "lsName2", ......etc};// map your method name to => key

	  if (map[request.method] !==null){
			sendResponse({data: localStorage[map[request.method]]}); //Send the localStorage data inside "response" if mapping exists
	  }
	}
 );
}

 

something like that perhaps, again this is off the top of my head.

Edited by __Darknite
Link to comment
Share on other sites

I have no idea. It took me bloody ages to get my head around the message passing itself (it's hardly elegant or user-friendly).

 

I've never come across a method like that before, but Google tells me that it's a shorthand way of creating objects.

 

Wouldn't that method be similar to what I considered - statically checking if the data exists?

Link to comment
Share on other sites

Basically the message passing works on callbacks, so you pass a function as a callback parameter. When your target function executes, it then executes your callback function that you passed.

 

 

I've had another look at your code,

 

Are you just trying to get a bunch of key value pairs off the extension and then loading them into the content side?

 

If that is the cause, you know you can store all the KV set as a json object and then just push that into and out of "localStorage"

 

so basically all you code would become:

 

extension:

 


function addMessageListeners(){
 chrome.extension.onRequest.addListener(
	function(request, sender, sendResponse) {
			sendResponse({data: localStorage[request.key]});
	}
 );
}


 

content

 


function passMessages(){
 chrome.extension.sendRequest({method: "ObjectKey"}, function(response) {
	localStorage["ObjectKey"] = response.data;
	[Call your Function Here Data has now been loaded];
 });

}

Edited by __Darknite
Link to comment
Share on other sites

The localStorage data is set on the options page and then needs to be passed to the functions that use them (which are in the content script). Google disallows the content script to have any form of communication with the extension itself, except through message passing. So I have to pass all the preferences through as messages.

 

Oh, I see. So you're suggesting that I send all of the data as one object it and then separate it on the other side? That could work, I suppose. I just can't use localStorage on the other side (I don't think it supports any data types other than String). If so, that still needs time to pass..

Edited by Myth of Echelon
Link to comment
Share on other sites

I really don't see how the mapped method name array thing could work, but funnelling the localStorage data into a JSON object, piping it through and then extracting it on the other end has intrigued me. Plus it would negate the need for the mapped array entirely, as I would only need one message to be passed.

 

The only thing that concerns me, though, is how I would go abouts extracting the values once the JSON object has been passed to the content script. As previously mentioned, I don't think localStorage supports any other data than Strings (based entirely off of me trying to enter a Boolean value into it and only being able to confirm true/false by matching it as a string), so how would I separate the values and differentiate what preference they represent if it's just one long string?

Edited by Myth of Echelon
Link to comment
Share on other sites

I really don't see how the mapped method name array thing could work, but funnelling the localStorage data into a JSON object, piping it through and then extracting it on the other end has intrigued me. Plus it would negate the need for the mapped array entirely, as I would only need one message to be passed.

 

 

Yes that is correct.

 

The only thing that concerns me, though, is how I would go abouts extracting the values once the JSON object has been passed to the content script. As previously mentioned, I don't think localStorage supports any other data than Strings (based entirely off of me trying to enter a Boolean value into it and only being able to confirm true/false by matching it as a string), so how would I separate the values and differentiate what preference they represent if it's just one long string?

 

localStorage is a simple KV store, and yes it only stores strings. This is why:

  • When you put JSON objects into localStorage you convert a json object into a string using JSON.stringyify
  • When you get an string out of localStorage you convert the string back into json object by using the JSON.parse method

 

Does that make more sense?

Edited by __Darknite
Link to comment
Share on other sites

So, just something like:

 

background.js

var values = JSON.stringify({"lsName1": localStorage["lsName1"], lsName2: localStorage["lsName2"]}); //No idea if that's right
function addMessageListeners(){
 chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
  if (request.method == "getJSONData"){
	sendResponse({data: values});
  }
}
 );
}

 

content.js

function passMessages(){
 chrome.extension.sendRequest({method: "getJSONData"}, function(response) {
var values = JSON.parse(response.data);
localStorage["lsName1"] = values.lsName1;
localStorage["lsName2"] = values.lsName2;

continueOn(); //I'm guessing that things inside this function are asynchronous, and, therefore, "continueOn()" will only be fired once the above operations have been completed?
 });
}

 

 

Sorry, this method and syntax is new to me. The above code was just a wild stab in the dark using what I could grasp. :|

Edited by Myth of Echelon
Link to comment
Share on other sites

Yes, that's almost spot on, except:

 

I don't see why you would need to parse the string at that point, you could simply stuff it into localStorage.

 

Its only when you need to access a particular attribute that you would parse the string.

 

Edit:

 

 


background (localStorage)                           content (localStorage)

k => ConfigData                                            k  => ConfigData
v => (json as string)	   <============>   v => (json as string)

Edited by __Darknite
Link to comment
Share on other sites

Not that I know of.

 

Are you storing all the values as a json object that has been turned into a string?

 

Hopefully if you have done that, then once you get this string back, you can turn this into a json object.

 

You can of course cycle through (for each) json element.

 

Does that make sense?

Edited by __Darknite
Link to comment
Share on other sites

ok, lets back up a little bit:

 

in your background.js:

 

var values = JSON.stringify({"lsName1": localStorage["lsName1"], lsName2: localStorage["lsName2"]});

 

this is telling me that you have each element stored separately.

 

I am suggesting, that is not needed. depending on how you are initially storing those elements, instead of storing them one by one. Store them all in big json object then convert it into a string and store that instead.

 

Is that a little clearer?

Link to comment
Share on other sites

Never mind, it doesn't matter. I can do without it. :L

 

Thanks for your help, though! Really, really appreciate it. :)

 

(Feel free to mark this closed now)

 

As you command

*Closes Thread*

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...