Обещайте дождаться Chrome.runtime.sendMessage
Я всегда видел, как Promise работает с setTimeout, но я пытаюсь сделать это на основе того, что chrome.runtime.sendMessage возвращает в Promise.
У меня есть скрипт контента, который выполняет эту функцию после того, как скрипт был выполнен.
chrome.runtime.sendMessage({complete: true});
У меня есть фоновый скрипт, который перебирает каждый элемент в массиве и использует одно из его значений для открытия URL-адреса с помощью chrome.tabs.update.
То, что я пытаюсь сделать, это заставить асинхронную функцию ждать сообщения, которое отправляет скрипт содержимого, и продолжить следующую итерацию только после получения сообщения, хотя я не знаю, как это реализовать, поскольку только видел примеры с setTimeout.
Так и должно быть
- Откройте первый элемент в массиве и остановитесь
- Выполните сценарий содержимого на этой странице и выполните sendMessage в конце.
- Теперь фоновый скрипт должен ожидать получения sendMessage, прежде чем перейти к следующему элементу.
- Как только sendMessage было получено с onMessage, оно должно перейти к следующему пункту и повторить с шага 2
Это фоновый скрипт.
chrome.storage.local.get('userItems', function(result) {
console.log(result.userItems);
function delay() {
// I suppose I should do something with onMessage in the delay function
return new Promise(resolve => setTimeout(resolve, 500));
}
async function delayedLog(item) {
await delay();
console.log(item);
var url = "http://www.example.com/" + item.Category;
chrome.tabs.update({
url: url
});
}
async function processArray(array) {
for (const item of array) {
await delayedLog(item);
}
}
processArray(result.userItems);
});
3 ответа
Проще попросить контент-скрипт выполнить свою работу и ответить, когда он закончится.
Чтобы "sendMessage" работал с обещаниями, вы можете обернуть его:
/**
* Promise wrapper for chrome.tabs.sendMessage
* @param tabId
* @param item
* @returns {Promise<any>}
*/
function sendMessagePromise(tabId, item) {
return new Promise((resolve, reject) => {
chrome.tabs.sendMessage(tabId, {item}, response => {
if(response.complete) {
resolve();
} else {
reject('Something wrong');
}
});
});
}
Контент-скрипт должен иметь что-то вроде этого:
// waiting for tasks from background
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
const item = msg.item;
// ..process your "item"
sendResponse({complete: true}); // telling that CS has finished its job
// 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).
return true;
});
На основе того, что было сказано выше, в следующем примере демонстрируется синхронная и асинхронная отправка ответа . Это сработало для меня, когда я смог отправить объект запроса в сценарий содержимого и получить объект ответа обратно из сценария содержимого.
В файле background.js добавьте следующий фрагмент, который оборачивает обещание вокруг функции sendMessage.
function sendMessage(tabId, request) {
return new Promise((resolve, reject) => {
chrome.tabs.sendMessage(tabId, request, (response) => {
if (response.success) {
resolve(response)
}
else {
reject(response)
}
});
})
}
const tabId = 123;
const request = {id: 1, message: "hello"};
const response = await sendMessage(tabId, request);
response.success ? console.info(response.data)
: console.error(response.message);
В вашем контентном скрипте:
const handleRequest = (request, sender, sendResponse) => {
console.info("Request received...");
if (request && request.id > 0) {
doLogic(request.id, sendResponse);
// return true to send the response asynchronously
return true;
}
else {
// send synchronous response object back with an error message
sendResponse({success: false, message: "Invalid Id received"});
}
}
chrome.runtime.onMessage.addListener(handleRequest);
В вашей функции doLogic:
const doLogic = async (id, sendResponse) => {
const value= await asyncOperation(id); // awaiting another async call
sendResponse({success: true, data: value});
};
Как показано выше, функция принимает
id
и
sendResponse
в качестве входов. Однажды
doLogic()
завершает операцию ожидания внутри, затем отправляет ответ.
Отправив true в слушателе, как упоминал user2724961, вы хотите отправить ответ асинхронно. Вы можете прочитать больше об этом здесь, где упоминается, как показано ниже:
Выполняя пример кода, вы получите следующий вывод. Измените объект запроса, чтобы увидеть изменения в выводе.
Выход:
world
В манифесте v3 вы можете сделать это:
function sendMessageToTab(tabId, message) {
return new Promise((resolve) => {
chrome.tabs.sendMessage(tabId, message, resolve)
})
}