9

I am writing a chrome extension with node module "chrome-extension-async" and meeting a problem when use await in the listener of background.

The content.js which will be injected into the page will send a message to the background, asking it to do some IO operations which is async:

// content.js
const package = await chrome.runtime.sendMessage({param: ...})
console.log(package)

// background.js
chrome.runtime.onMessage.addListener(async (request, sender, 
sendResponse) => {
    const value  = await doIOOperation();
    sendResponse(value);
})

However, chrome will report errors like below:

Uncaught (in promise) Error: The message port closed before a response was received.

I think there must be some conflict when using async/await in the listener, Anyone know how to solve this problem?

pppery
  • 3,731
  • 22
  • 33
  • 46
Kyle Hu
  • 111
  • 1
  • 1
  • 5
  • 6
    I think you need to add `return true` just like with the [classic callback version](https://stackoverflow.com/a/20077854), but the listener can't be `async` - you'll have to put the async code into IIFE or another function. I also think the [WebExtension polyfill](https://github.com/mozilla/webextension-polyfill) is better and BTW it correctly handles this case. – wOxxOm Jan 03 '19 at 07:04
  • @wOxxOm `sendResponse` is planned to be phased out from the W3C draft. And it is to be replaced by returning a Promise. I'm assuming this is now happening on Chrome, with some users reporting that this issue is happening on Chrome 71. https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage – tom_mai78101 Jan 11 '19 at 14:20
  • 1
    @tom_mai78101, nope, W3C is not related to Chrome API, which is not Promise-based. The OP simply uses a promisifier library, which apparently confused you. – wOxxOm Jan 11 '19 at 14:40
  • @wOxxOm Ahh, sorry my mistake. Thanks for the clarification. – tom_mai78101 Jan 11 '19 at 15:08
  • As per @wOxxOm's comment - see https://stackoverflow.com/a/20077854/868159 – oatsoda Feb 26 '20 at 08:33

2 Answers2

11
const asyncFunctionWithAwait = async (request, sender, sendResponse) => {...}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    asyncFunctionWithAwait(request, sender, sendResponse)

    return true
})

worked for me

RomanistHere
  • 795
  • 7
  • 17
  • This worked for me as opposed to directly passing a handler that returns a promise. – ealfonso Jan 23 '21 at 17:52
  • Thank for this. Quite convoluted to work on building Chrome extensions, especially with Manifest 3, the documentation is also incomplete. – atulmy Jul 14 '21 at 09:33
  • 6
    This would be a better answer if you explained how the code you provided answers the question. – pppery Mar 18 '22 at 02:23
4

To expand on all the notes and clarify @RomanistHere's answer:

// a couple example async example functions
var greet;
const whatever = async function(){
    greet = "hello";
}
const getSomething = async function(){
    return "bob";
}

// set up a function outside the callback, 
// ... giving freedom to leverage async/await.
const asyncFunctionWithAwait = async (request, sender, sendResponse) => {
    
    // now we can do await's normally here
    await whatever();
    let name = await getSomething();
    
    // since our response function is carried through "by reference",
    // therefore we can call it as if it were within the listener callback.
    sendResponse(greet + name)

}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {

    // exec in the body of this callback,
    // ... passing original function through.
    asyncFunctionWithAwait(request, sender, sendResponse)

    // per:
    // http://developer.chrome.com/extensions/runtime.html#event-onMessage
    //
    // This function becomes invalid when the event listener returns, 
    // unless you return true from the event listener to indicate you 
    // wish to send a response asynchronously (this will keep the message 
    // channel open to the other end until sendResponse (3rd arg) is called).
    return true;

});
bob
  • 7,539
  • 2
  • 46
  • 42