How to send a message to another part of the browser extension – asynchronously

Erros I got when send a message asynchronously

I got some errors related to messaging from a content script to another part of the extension for example a background JavaScript.

These are the errors I got,

  • Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
  • Error sending message: The message port closed before a response was received.

I found I need to understand how to send a message to another part of the browser extension asynchronously.

Then I will be able to know how to solve these problems.

How to send a message in Browser Extension in Manifest V3

I learned this with Chrome Extension API – Message passing

What I found was that,
On the receiving side (the onMessage listener), sendResponse is always used to send a reply. However, on the sending side (where sendMessage is called), you can choose to receive the response in callback or promise.

But you can’t use a promise and a callback in the same call.

This is a way to get a response by using callback.

// Content Script (sending side)
chrome.runtime.sendMessage({ action: "getData" }, (response) => {
    console.log("Response received:", response);
});

This is a way to get a response by using promise.

// Content Script (sending side)
(async () => { const response = await chrome.runtime.sendMessage({ action: "getData" }); console.log("Response received:", response); })();

To send a single message, we need to use chrome.runtime.sendMessage() or tabs.sendMessage().

How to receive a message and response in Browser Extension in Manifest V3

If you want to do it asynchronously, you must return a literal true (return true😉 from the event listener. Doing so will keep the message channel open to the other end until sendResponse is called.

This is a sample code for the receiving side that returns value asynchronously.

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "getData") {
        setTimeout(() => {
            sendResponse({ success: true, data: "Delayed Response!" });
        }, 1000);
        return true; // support asynchronous
    }
});

I came across a note stating that the listener function should not be an async function. This is likely because returning a Promise and returning true or false might conflict and cause unexpected behavior.


Comments

One response to “How to send a message to another part of the browser extension – asynchronously”

Leave a comment