chrome extension - sendResponse not waiting for async function
The callback of onMessage should return a literal true
value (documentation) in order to keep the internal messaging channel open so that sendResponse can work asynchronously.
Problem
Your callback is declared with async
keyword, so it returns a Promise
, not a literal true
value. Chrome extensions API doesn't support Promise in the returned value of onMessage callback until https://crbug.com/1185241 is fixed so it's just ignored, the port is immediately closed, and the caller receives undefined
in response.
Solution
Remove the async
keyword and use a standard function, from which you can call another async function that can be embedded as an IIFE:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "get_thumbnails") {
(async () => {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload});
})();
return true; // keep the messaging channel open for sendResponse
}
});
Or declare a separate async
function and call it from the onMessage listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.message === "get_thumbnails") {
processMessage(msg).then(sendResponse);
return true; // keep the messaging channel open for sendResponse
}
});
async function processMessage(msg) {
console.log('Processing message', msg);
// .................
return 'foo';
}
How to handle async requests when message passing within chrome extensions
fetchListTitles
is declared with async
keyword which means it always returns a Promise.
Chrome extensions can't send Promise via messaging.
You need to send the response after the Promise is fullfilled:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.command === 'fetch') {
fetchListTitles().then(listData => sendResponse({
type: 'result',
status: 'success',
data: listData,
request: msg,
}));
return true; // keeps the channel open for sendResponse
}
});
See also why Chrome extensions can't use async/await in onMessage directly.
Response is undefinded with async, await, promise
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).
From here
But using async-await, your function will return a Promise.
So you want to do this:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
chrome.downloads.search({id: files[i]},
function (item) {
sendResponse(item[0].id);
}
);
return true;
});
But really, you want to do this:
chrome.runtime.onMessage.addListener(
(req, sender, sendResponse) => {
chrome.downloads.search({id: files[i]},
item => sendResponse(item[0].id);
);
return true;
});
Or at least, I do! :-)
For the looping. You want to return an array of files. So what you do in your program is:
- Create an array of "A Promise of a file or undefined"
- Wait for all those Promises to resolve.
- Filter out the undefined.
- Return the array of files.
chrome.runtime.onMessage.addListener(
(req, sender, sendResponse) => {
if (req !== "files") { return } // why wait?
Promise.all(files.map(file =>
new Promise(resolve =>
chrome.downloads.search({id: file},
item => resolve(item[0].exists ?
{
filename: [item[0].filename,
id: item[0].id]
} : undefined)))))
.then(res => res.filter(i => !!i)) //remove empty
.then(sendResponse)
return true;
});
Basically, if you are using loops or variables, you're doing it wrong. Constants and functions only.
Hope this helps.
chrome extension - how i can await for chrome.runtime function?
you can't use both a callback in a
chrome
method and await on the returned value because when a callback is used the method won't return a Promise.bug in Chrome before 99: sendMessage doesn't return a Promise so you can't await it.
Fixed in Chrome 99.
So, in earlier versions of Chrome you can promisify the API:
promisifyApi(chrome.runtime, 'sendMessage');
(async () => {
const res = await chrome.runtime.sendMessage({ input });
console.log(res);
})();
function promisifyApi(thisArg, name) {
const fn = thisArg[name];
const localStack = new Error('Before calling ' + name);
thisArg[name] = (...args) => new Promise((resolve, reject) => {
fn(...args, result => {
let err = chrome.runtime.lastError;
if (err) {
err = new Error(err.message);
err.stack += '\n' + localStack.stack;
reject(err);
} else {
resolve(result);
}
});
});
}
...or use a callback:
chrome.runtime.sendMessage({ input }, res => {
// process `res` here
});
Chrome extension: chrome local storage is not set instantly
The solution is to use messaging correctly so you can wait on the result reliably.
- remove chrome.storage, it's not necessary for this task;
- remove
async
from onMessage listener (why?) and use a separate async function to get info; - return the result via
sendResponse
+return true
.
content.js:
chrome.runtime.sendMessage(url, res => {
console.log(res);
if (res.html) {
// use `res.html` here
}
});
background.js:
const API_GET = 'http://localhost:8000/get.php?url=';
const API_READ = 'http://localhost:8000/read.php?url=';
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
getInfo(request).then(sendResponse);
return true; // keep the channel open for asynchronous sendResponse
});
async function getInfo(url) {
const res = JSON.parse(await sendReq(`${API_GET}${url}`));
return res.status === 'ok' ? {
rules: res.msg.rules,
html: await sendReq(`${API_READ}${url}`),
} : {};
}
Related Topics
How to Add an Object to an Array
Merge JavaScript Objects in Array with Same Key
Check If an Element Is Present in an Array
JavaScript - Get Minutes Between Two Dates
Js:Convert Array of Strings to Array of Objects
Why Is Requestanimationframe Better Than Setinterval or Settimeout
One Liner to Flatten Nested Object
Basic JavaScript Promise Implementation Attempt
Get Functions (Methods) of a Class
Check If String Contains Only Digits
How to Call 3 Functions in Order to Execute Them One After the Other
How to Toggle an Element's Class in Pure JavaScript
What Is "Export Default" in JavaScript
Can a PDF File's Print Dialog Be Opened with JavaScript
Dangerous Implications of Allman Style in JavaScript
Differencebetween JavaScript Promises and Async Await