Chrome Extension - Sendresponse Not Waiting for Async Function

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:

  1. Create an array of "A Promise of a file or undefined"
  2. Wait for all those Promises to resolve.
  3. Filter out the undefined.
  4. 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?

  1. 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.

  2. 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.

  1. remove chrome.storage, it's not necessary for this task;
  2. remove async from onMessage listener (why?) and use a separate async function to get info;
  3. 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



Leave a reply



Submit