Обещайте дождаться Chrome.runtime.sendMessage

Я всегда видел, как Promise работает с setTimeout, но я пытаюсь сделать это на основе того, что chrome.runtime.sendMessage возвращает в Promise.

У меня есть скрипт контента, который выполняет эту функцию после того, как скрипт был выполнен.

chrome.runtime.sendMessage({complete: true});

У меня есть фоновый скрипт, который перебирает каждый элемент в массиве и использует одно из его значений для открытия URL-адреса с помощью chrome.tabs.update.

То, что я пытаюсь сделать, это заставить асинхронную функцию ждать сообщения, которое отправляет скрипт содержимого, и продолжить следующую итерацию только после получения сообщения, хотя я не знаю, как это реализовать, поскольку только видел примеры с setTimeout.

Так и должно быть

  1. Откройте первый элемент в массиве и остановитесь
  2. Выполните сценарий содержимого на этой странице и выполните sendMessage в конце.
  3. Теперь фоновый скрипт должен ожидать получения sendMessage, прежде чем перейти к следующему элементу.
  4. Как только 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)
    })
}