I want to use async await in an onMessage listener:
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) =>{
var key = await getKey();
sendResponse(key);
});
However I get undefined when I send a message.
From the documentation for chrome.runtime.onMessage.addListener:
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 is called).
This works when I use a callback.
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) =>{
getKey(key => {
sendResponse(key);
});
return true;
});
However I would like to leverage the await syntax. But it does not seem to work and still returns undefined:
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) =>{
var key = await getKey();
sendResponse(key);
return true;
});
Extract to an async function.
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
doSomethingWith(request).then(sendResponse);
return true; // return true to indicate you want to send a response asynchronously
});
async function doSomethingWith(request) {
var key = await getKey();
// await .....
return key;
}
The return value of an async
function is implicitly wrapped in Promise.resolve
. See async doc.
The return true;
statement did the trick. It tells Chrome that you want to send a response asynchronously.
See onMessage.
If you feel that you will use it most frequently, create a utility, something like:
const wrapAsyncFunction = (listener) => (request, sender, sendResponse) => {
// the listener(...) might return a non-promise result (not an async function), so we wrap it with Promise.resolve()
Promise.resolve(listener(request, sender)).then(sendResponse);
return true; // return true to indicate you want to send a response asynchronously
};
chrome.runtime.onMessage.addListener(
wrapAsyncFunction(async (request, sender) => {
console.log(request, sender);
const key = await getKey();
// await .....
return key;
})
);
Use mozilla/webextension-polyfill if you prefer to "cross browsers" extension.
Example:
var browser = require("webextension-polyfill");
browser.runtime.onMessage.addListener(async (msg, sender) => {
console.log("BG page received message", msg, "from", sender);
console.log("Stored data", await browser.storage.local.get());
});
browser.browserAction.onClicked.addListener(() => {
browser.tabs.executeScript({file: "content.js"});
});
Not sure if Chrome extension runtime environment supports the async/await syntax, but you can use a transpiler (i.e. Babel) to have it converted to for example ES5. Then you can define a wrapper function like this:
function asChromeListener(listener) {
return (message, sender, sendResponse) => {
const returnValue = listener(message, sender);
if (isPromise(returnValue)) {
returnValue.then(sendResponse);
return true;
}
else {
if (typeof returnValue !== 'undefined') {
sendResponse(returnValue);
}
return false;
}
};
}
function isPromise(value) {
return typeof value === 'object' && value !== null && 'then' in value && 'catch' in value;
}
Which you can then use like:
chrome.runtime.onMessage.addListener(asChromeListener(async (message, sender) => {
return await doMyAsyncWork(message);
});
Since we use TypeScript, here's also the snippet that we actually use (with generic types).
export function asChromeListener<M, S, R extends Function>(listener: (message: M, sender: S) => any) {
return (message: M, sender: S, sendResponse: R) => {
const returnValue = listener(message, sender);
if (isPromise(returnValue)) {
returnValue.then(sendResponse);
return true;
}
else {
if (typeof returnValue !== 'undefined') {
sendResponse(returnValue);
}
return false;
}
};
}
function isPromise(value: any) {
return typeof value === 'object' && value !== null && 'then' in value && 'catch' in value;
}
Function to call (at most once) when you have a response. The argument should be any JSON-ifiable object. If you have more than one onMessage listener in the same document, then only one may send a response. 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 is called).
https://developer.chrome.com/docs/extensions/reference/runtime/#event-onMessage
have to say the official doc is really hard to read.
The closest I could get:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
(async () => {
var key = await getKey();
sendResponse(key);
})();
return true;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With