I am fairly new at both JavaScript and Chrome Extensions but am developing a halfway decent understanding of manifest version 3, promises/async/await and message passing from the content script to the service worker. And I've tried reading a lot of posts on related topics. And yet, I still can't get asynchronous promises to work in order to send a message from a content script to a background (service worker) script, execute an async function, and then deliver a response.
The documentation makes it look doable, but still reads as slightly cryptic to me:
Note: The sendResponse callback is only valid if used synchronously, or if the event handler returns true to indicate that it will respond asynchronously. The sendMessage function's callback will be invoked automatically if no handlers return true or if the sendResponse callback is garbage-collected.
I don't exactly know what garbage-collected means and I only think I understand where and how it expects the event handler to "return true" from other responses like this one and this one. Anyway, my message clearly makes it to the service worker, which resolves the promise and gets information, but then isn't able to successfully deliver the response to the content script. So which piece of this puzzle am I still not getting? Something fundamental or a minor mistake? About promises, chrome, or both? Thanks.
FYI:
- I've tried disabling all other extensions.
- I've reloaded the extension and reloaded the tab
- Google Chrome Version 95.0.4638.69 (Official Build) (64-bit)
- MS Windows 10.0
Code: (minimum viable product to demonstrate issue)
background.js (service worker)
function myTestPromise() {
return new Promise((resolve)=> {
console.log("I'm working async. Give me half a sec.") // returns phrase (inspect view: service worker)
setTimeout(()=> {resolve("Success")}, 500)
})
}
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
if (msg.message=="Please Respond") {
const testMessage = await myTestPromise();
// const testMessage = "Success" <-- if I use this instead (sync) it works as expected
console.log(testMessage) // returns "Success"
sendResponse({response:testMessage})
}
return true; // Right? It doesn't help to remove it, fwiw.
});
mORS.js (content script)
chrome.runtime.sendMessage({ message: "Please Respond" }, (response)=>{
console.log(`Response: ${response}`) // returns "Response: undefined"; expecting "[object: object]"
try {
if (response.response=="Success") {
console.log("Success!") // Nope
} catch (error) {
console.log(error) /*
Returns "TypeError: Cannot read properties of undefined (reading 'response') at mORS.js:665
Also generates error: "Unchecked runtime.lastError: The message port closed before a response was received."
*/
}
})
manifest.json
{
"manifest_version": 3,
"name": "ORS Extension",
"version": "1.15",
"action": {
"default_icon": {
...etc..
},
"default_title": "mORS",
"default_popup": "popup.html"
},
"content_scripts":[
{
"matches": ["*://www.oregonlegislature.gov/bills_laws/ors/ors*.html*"],
"js": ["mORS.js"]
}
],
"background": {
"service_worker": "background.js"
},
"icons": {
...etc...
},
"permissions": [
"storage",
"activeTab",
"tabs",
"notifications",
"scripting"
],
"host_permissions":
["*://www.oregonlegislature.gov/bills_laws/ors/ors*.html*"]
}