I want to build a chrome extension which crawl (partial) an website. From popup I want to call an async method from background where I do some blocking requests. After this call I want to show a status in popup by requesting some information on setInterval... After I start the process, the popup is locked until background process ends. I tried many solutions like return true after background received a message, launch an async process in background but I have no solution for this... Here is my code:
Manifest
{
  "manifest_version": 2,
  "name": "...",
  "description": "...",
  "version": "1.0",
  "browser_action": {
   "default_icon": "logo_76.png",
   "default_popup": "main.htm"
  },
  "background": {
    "scripts": [
                "lib/regex_utils.js",
                "lib/uri.js",
                "lib/uri_utils.js",
                "lib/bloomfilter.js",
                "background.js"
                ],
    "persistent": true
  },
  "content_scripts":[{
        "matches": ["*://*/*"],
        "js":      ["content.js"]
    }],
  "permissions": [
    "activeTab",
    "http://*/*",
    "https://*/*"
    ]
}
Popup
var t = null;
var port = null;
function showStatus(response){
    // document.getElementById('start').innerHTML = 'parsed ' + response['parsed']
    console.log(response);
}
function checkStatus(){
    console.log('check')
    if(port){
        var data = {};
        data['command'] = 'status';
        port.postMessage(data); 
    }
}
clearInterval(t); t = setInterval(checkStatus, 2000);
document.addEventListener('DOMContentLoaded', function() {
    var checkPageButton = document.getElementById('start'); 
    checkPageButton.addEventListener('click', function() {
        chrome.tabs.getSelected(null, function(tab) {
            port = chrome.extension.connect({name: "communication"});
            var data = {}
            data['command'] = 'start_crawl'
            data['url'] = tab['url']
            port.postMessage(data);
            port.onMessage.addListener(function(msg) {
                if(msg['parsed'] != undefined){
                    showStatus(msg);                    
                }
            });
        });
  }, false);
}, false);
Bakground
function getLinksFromUrl(url){
    var xhr = new XMLHttpRequest();
    var data = null;
    var base = null;
    xhr.open("GET", url, false);
    function handleStateChange() {
      if (xhr.readyState == 4 && xhr.status==200) {
        data = xhr.responseText;
      }
    }
    xhr.onreadystatechange = handleStateChange;
    try {
        xhr.send();
    }catch(err){
        console.log('request exception');
    }
    if(data){
        linkRegex = new RegExp('a[^>]+?href=["\']{0,1}([^"\'\\s>]+)','igm');
        baseRegex = new RegExp('base[^>]+?href=["\']{0,1}([^"\'\\s>]+)','igm');
        var matches = linkRegex.execAll(data);  
        var base = baseRegex.exec(data);
        if (base && base[1] != undefined){
            base = base[1]
        }
        var rawLinks = matches.map(function(e){
            return e[1];
        });
        // Check to be on the same
        var finalLinks = relative2absolute(rawLinks, url, base).filter(function(e){
            var uri = new URI(e);
            if(ROOT_DOMAIN == uri.domain()){
                return true;
            }
            return false;
        });
        return finalLinks;
    }
    return [];
}
var TO_CRAWL = [];
var ALL_UNIQUE_URLS = []
var PASRSED_URLS = new BloomFilter();
var PAGE_LIMIT = 150;
var PARSED = 0;
var ROOT_DOMAIN = '';
function async(fn, callback) {
    setTimeout(function() {
        fn();
        callback();
    }, 0);
}
function getStatus(){
    var data = {}
    data['parsed'] = PARSED
    data['unique'] = ALL_UNIQUE_URLS.length
    data['queue'] = TO_CRAWL.length
    return data;
}
function crawl(initialUrl){
    var uri = new URI(initialUrl)
    ROOT_DOMAIN = uri.domain()
    TO_CRAWL = []
    ALL_UNIQUE_URLS = []
    PASRSED_URLS = new BloomFilter()
    PARSED = 0
    TO_CRAWL = getLinksFromUrl(initialUrl);
    while(ALL_UNIQUE_URLS.length < PAGE_LIMIT && TO_CRAWL.length > 0){
        url = TO_CRAWL.pop();
        var links = getLinksFromUrl(url)
        // TODO Add extra check for 404 and other error codes
        // Mark current URL as crawled
        PARSED++;
        PASRSED_URLS.add(url);
        ALL_UNIQUE_URLS.push(url);
        // Add new URls in queue
        links = links.filter(function(e){
            // TODO check domain here
            if(!PASRSED_URLS.check(e)){
                return true;
            }
            return false;
        });
        TO_CRAWL.push.apply(TO_CRAWL, links);
    }
}
chrome.extension.onConnect.addListener(function(port) {
  port.onMessage.addListener(function(data) {
    if (data['command'] != undefined && data['command'] == 'start_crawl'){
            async(
                    function(){
                        crawl(data['url']);
                    },
                    function(){
                        console.log(ALL_UNIQUE_URLS);
                    }
                );
            // SECOND METHOD TO SEND DATA AT POPUP
            var interval = setInterval(function(){
                port.postMessage(JSON.stringify(getStatus()));
            }, 1000);
        console.log('started')
        port.postMessage('crawling started');   
    }
    // FIRST METHOD TO SEND DATA AT POPUP
    if (data['command'] != undefined && data['command'] == 'status'){
        port.postMessage(JSON.stringify(getStatus()));
    }        
  });
});
 
    