简体   繁体   中英

React Chrome extension and Promises

I am writing a Chrome extension in ReactJS.

I am looping through an array of URLs and trying to get the the HTML content of those pages.

this.state.advertData.map(function(e, i) {

    common.updateTabUrl(e.url).then((tab) => {

        common.requestHTML(tab).then((response) => {

            console.log(response.content);

        })

    });

})

common.js:

let requestHTML = function(tab) {

    return new Promise(function(resolve, reject) {

        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            chrome.tabs.sendMessage(tab.id, {'req': 'source-code'}, function (response) {

                resolve(response)

            })
        })

    })

}

let updateTabUrl = function(url) {

    return new Promise(function(resolve, reject) {

        let update = chrome.tabs.update({
            url: url
        }, function(tab) {
            chrome.tabs.onUpdated.addListener(function listener (tabId, info) {
                if (info.status === 'complete' && tabId === tab.id) {
                    chrome.tabs.onUpdated.removeListener(listener);
                    resolve(tab);
                }
            });


        })

    })
}

content_script.js

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
    let response = '';
    if (request.req === 'source-code') {
        response = document.documentElement.innerHTML;
    }
    sendResponse({content: response});
});

My issue is that the response.content always seems to be the same. More importantly, the tab that updates seems to only ever display the last url in my array. I think it is a problem with the way I am handling Promises.

Any help is appreciated.

The problem with your code is that it doesn't wait for the previous URL to load before proceeding to the next one so only the last one gets actually loaded in a tab.

I suggest using 1) Mozilla's WebExtension polyfill , 2) await/async syntax, 3) executeScript that automatically runs when a tab is complete by default 4) a literal code string in executeScript so you don't need neither a separate file nor to declare the content script in manifest.json.

async function getUrlSourceForArray({urls, tabId = null}) {
  const results = [];
  for (const url of urls) {
    await browser.tabs.update(tabId, {url});
    const [html] = await browser.tabs.executeScript(tabId, {
      code: 'document.documentElement.innerHTML',
    });
    results.push(html);
  }
  return results;
}

Invoking inside an async function:

const allHtmls = await getUrlSourceForArray({
  urls: this.state.advertData.map(d => d.url),
  tabId: null, // active tab
});

PS you can also open all the URLs at once in a new window in background, assuming there won't be more than say 10 URLs, otherwise you would risk exhausting the user's RAM.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM