【问题标题】:Make Promise wait for a Chrome.runtime.sendMessage让 Promise 等待 Chrome.runtime.sendMessage
【发布时间】:2022-03-26 02:59:37
【问题描述】:

我一直看到 Promise 与 setTimeout 一起工作,但我试图根据 chrome.runtime.sendMessage 返回到 Promise 的任何内容来实现它。

我有一个内容脚本,一旦脚本完成,它就会执行此功能。

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

我有一个后台脚本,它循环遍历数组中的每个项目,并使用其中一个值打开带有 chrome.tabs.update 的 URL。

我想做的是让异步函数等待内容脚本发送的消息,并且只有在收到消息后才继续下一次迭代,尽管我不知道如何实现这一点,因为我'只见过 setTimeout 的例子。

应该是这样的

  1. 打开数组中的第一项并停止
  2. 在该页面上执行内容脚本并在最后执行 sendMessage。
  3. 现在后台脚本应该等待 sendMessage 被接收,然后再转到下一个项目。
  4. 一旦使用 onMessage 接收到 sendMessage,它应该转到下一个和项目并从第 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);

    });

【问题讨论】:

  • 你可以简单地加载 Mozilla 的 WebExtensions polyfill 并使用 browser.runtime.sendMessage 等等,它会产生一个 Promise 以便你可以等待它。

标签: javascript google-chrome google-chrome-extension async-await


【解决方案1】:

要求内容脚本完成工作并在完成后回答会更容易。
要使“sendMessage”与 Promise 一起工作,您可以包装它:

/**
 * 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;

    // Asynchronously process your "item", but DON'T return the promise
    asyncOperation().then(() => {
      // telling that CS has finished its job
      sendResponse({complete: true});
    });

    // 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;
});

【讨论】:

  • 我尝试使用上面的代码。我发现 response.complete 不存在(至少对我来说)。我能够以 response 作为唯一的操作数来调用 resolve 函数。这对我有用。请注意,Chrome 回调仅(这可能不是真的)在成功案例中调用,因此 resolve(response) 可能是唯一要执行的正确代码。
【解决方案2】:

借鉴上面分享的内容,以下示例演示了同步和异步发送响应。这对我有用,我能够将请求对象发送到内容脚本并从内容脚本接收响应对象。


在你的 background.js 中,添加以下 sn-p,它将 promise 包装在 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});
};

如上所示,doLogic() 函数将idsendResponse 作为输入。一旦doLogic() 完成内部的等待操作,它就会发送响应。

Denis L 所述,通过在侦听器中发送true,您希望异步发送响应。您可以阅读更多关于 here 的内容,其中提到如下所示:

执行示例代码,您将收到以下输出。修改请求对象以查看输出的变化。

输出: world

【讨论】:

    【解决方案3】:

    在清单 v3 中,您可以这样做:

    function sendMessageToTab(tabId, message) {
        return new Promise((resolve) => {
            chrome.tabs.sendMessage(tabId, message, resolve)
        })
    }
    

    【讨论】:

      猜你喜欢
      • 2017-07-20
      • 2020-01-15
      • 2022-07-20
      • 2021-06-30
      • 2018-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多