从后台脚本在sendResponse中丢失数组数据

问题描述 投票:1回答:1

我需要从内容脚本内部访问来自我的API的数据,但是由于它们是HTTP请求(而不是HTTPS),因此它们被阻止在内容脚本内部。

因此,我正在后台脚本中进行所有请求,并尝试使用消息API在backgroundscript和contentscript之间进行通信。每当我准备使用contentscript中的数据时,我都会向后台脚本发送一条消息,然后该脚本将从API提取数据并将其作为对contentscript的响应发送。从后台脚本中,如果我在发送数据之前立即console.log数据,那么一切都很好(4个位置的数组)。但是,内容脚本中收到的数据是一个空数组,存储在该数组中的所有数据都会丢失。

这是发送消息的内容脚本代码段:

if (typeof chrome.app.isInstalled !== 'undefined')
{
    console.log("gbdScreen sending requests")
    chrome.runtime.sendMessage({metric: "issues"}, function(response) 
    {
        setTimeout(function(){
            if (response !== undefined)
            {
                console.log(response)
                console.log(response.data)
            }
            else{
                console.log("gbdScreen-else")
                document.getElementById('gbdButton').click()
            }         
        }, 2000)
    })
}

这是后台脚本,它在其中接收消息,继续获取数据,然后将其发送回:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) 
{
    let arr = []
    chrome.storage.sync.get('oauth2_token', function(res) 
    {
        if (res.oauth2_token != undefined)
        {
          chrome.tabs.query
          ({
              'active': true, 'lastFocusedWindow': true
          },
          function (tabs) 
          {
              let url = tabs[0].url.split("/")
              let owner = url[3]
              let repo = url[4].split("#")[0]
              let url_aux = `?owner=${owner}&repository=${repo}&token=${res.oauth2_token}`

              let url_fetch = url_base + '/commits' + url_aux

              // async function to make requests
              const asyncFetch = async () => await (await fetch(url_fetch)) 

              // commits request
              asyncFetch().then((resp) => resp.json()).then(function(data)
              {
                arr[0] = data
              }).catch(function(err)
              { 
                  console.log("Error: URL = " + url_fetch + "err: " + err)
              })

              // issues request
              url_fetch = url_base + '/issues' + url_aux
              asyncFetch().then((resp) => resp.json()).then(function(data)
              {
                arr[1] = data
              }).catch(function(err)
              { 
                  console.log("Error: URL = " + url_fetch + "err: " + err)
              })

              // branches request
              url_fetch = url_base + '/branches' + url_aux
              asyncFetch().then((resp) => resp.json()).then(function(data)
              {
                arr[2] = data
              }).catch(function(err)
              { 
                  console.log("Error: URL = " + url_fetch + "err: " + err)
              })

              // prs
              url_fetch = url_base + '/pullrequests' + url_aux
              asyncFetch().then((resp) => resp.json()).then(function(data)
              {
                arr[3] = data
              }).catch(function(err)
              { 
                  console.log("Error: URL = " + url_fetch + "err: " + err)
              })
              console.log(arr)
              sendResponse({data: arr}) // sends back to screen.js the data fetched from API
          })
        }
    })
    return true
})

我在backgroundscript和contentscript中都使用console.log,在backgroundscript中都很好,但是在contentscript中打印一个空数组。如果有人可以说清楚一点,我现在知道这是非常混乱的代码。

javascript html google-chrome google-chrome-extension google-chrome-app
1个回答
0
投票

该问题基本上是Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference的重复。

您长时间长时间异步填充数组之后,您正在发送空的初始[]。至于console.log,您仅看到最终数组,因为devtools按需读取变量-when you expand the value, not when console.log runs

解决方案是使用Promise.all等待所有提取完成,然后才发送响应。

让我们使用Mozilla's WebExtension Polyfill简化您的代码,以便代替return truesendResponse,我们可以从onMessage侦听器返回一个Promise,或者甚至更好地使用async/await

const FETCH_TYPES = [
  'commits',
  'issues',
  'branches',
  'pullrequests',
];

async function fetchJson(type, aux) {
  const url = `${url_base}/${type}${aux}`;
  try {
    return (await fetch(url)).json();
  } catch (err) {
    console.log('Error: URL =', url, 'err:', err);
  }
}

browser.runtime.onMessage.addListener(async (request, sender) => {
  const {oauth2_token} = await browser.storage.sync.get('oauth2_token');
  if (oauth2_token) {
    const url = sender.tab.url.split('/');
    const owner = url[3];
    const repo = url[4].split('#')[0];
    const aux = `?owner=${owner}&repository=${repo}&token=${oauth2_token}`;
    const data = await Promise.all(FETCH_TYPES.map(type => fetchJson(type, aux)));
    return { data };
  }
});

P.S。请注意代码如何使用sender.tab.url,因为消息可能来自非活动选项卡。

© www.soinside.com 2019 - 2024. All rights reserved.