After refactoring my code to ES6 modules, I’ve noticed a different behavior in execution of the main script.
The use case is simple - create new tab and send it a message:
// background.js
const tab = await browser.tabs.create({url: `my_page.html`});
await browser.tabs.sendMessage(tab.id, {type: 'openSearch'});
// my_page.js
browser.runtime.onMessage.addListener(data => {/*...*/}); // top level
Before - this was working fine, because I register onMessage
handler on the top level - when script is executed.
After migration to Modules - sending message fails with:
Error: “Could not establish connection. Receiving end does not exist.”
This is because the message handler is obviously not yet registered. And if I insert some delay after the tab creation, it will work again.
Is there an easy way to fix this? To wait for tab to be ready for messages.
EDIT:
After testing this further, I’ve found out that it’s actually not guaranteed to work at all even without modules involved. This kind of behavior works only with executeScript
when you can await for the script to run. But await browser.tabs.create
obviously won’t wait for the script to run.
(the fact that it “worked” before, is due to good fallback error handling in my app and bad error logging that hide it )
So again, any easy way to wait for the script to load?
I can think of these:
- nasty inline solution:
while (false === await browser.tabs.sendMessage(tab.id, 'ping').catch(() => false)) {} // WARNING: possible infinite loop!
-
sending some “ready” message when the script got executed - but it feels too spaghetti code since I need to watch for the message in background script…
-
use
webNavigation
ortabs.onUpdated
API - a bit too complex to do it right