The following setup of Message Passing for communicating JSON data from a content script to a popup script is pretty usual:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (sender.tab) {
return;
}
switch (request) {
case 'selected_count':
const count = selected_count();
console.log('selected_count:', count);
sendResponse({
data: count
});
break;
case 'selected_items':
const items = selected_items();
console.log('selected_items:', items);
sendResponse({
data: items
});
break;
case 'unselect_all':
unselect_all();
break;
}
});
However, if some ´items´ contain data that you get asynchronously (like an image), then the setup above will fail if those data are not loaded before the handler returns.
Unfortunately, ´Promise´s are the right tool for the job in this case, but you can’t use them right away, because you can’t send a promise back to the popup and let it handle that, because a message can’t contain a promise, because
A message can contain any valid JSON object (null, boolean, number, string, array, or [my annotation: POJO] object).
Fortunately, according to the documentation,
If you want to asynchronously use ´sendResponse´, add ´return true;´ to the ´onMessage´ event handler.
which you can do like I show in the rewrite below, where I use a ´Promise´ object to take care of the asynchronous bits:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
const KEEP_CHANNEL_OPEN = true;
const CLOSE_CHANNEL = false;
var result = CLOSE_CHANNEL;
if (sender.tab) {
return result;
}
switch (request) {
case 'selected_count':
const count = selected_count();
console.log('selected_count:', count);
sendResponse({
data: count
});
break;
case 'selected_items':
selected_items().then(items => {
console.log('selected_items:', items);
sendResponse({
data: items
});
});
result = KEEP_CHANNEL_OPEN;
break;
case 'unselect_all':
unselect_all();
break;
}
return result;
});