1

在我的 Chrome 扩展程序中,我有一组 URL,我想找到第一个未访问的 URL。因为chrome.historyAPI 是异步的,我的第一反应是做一些怪诞的递归迭代,像这样......

urls = [...];
function recur(idx) {
    chrome.history.getVisits(urls[idx], function(visitItems) {
        if(visitItems && visitItems.length > 0) {
            // Success!
        } else {
            recur(idx + 1);
        }
    }
}
recur(0);

但是,这很糟糕(它真的很难看,它可能很慢,而且它会因为长列表而中断)。

有什么方法可以更好地处理所有这些对 chrome.history 的调用?或者,是否有完全不同的选择?

4

1 回答 1

1

如果顺序很重要,并且您的列表很长,并且迟早找到未访问链接的可能性很高,那么最好的方法基本上就是做您正在做的事情。这是forEachSeries一个流行async库的实现。

async.forEachSeries = function (arr, iterator, callback) {
    callback = callback || function () {};
    if (!arr.length) {
        return callback();
    }
    var completed = 0;
    var iterate = function () {
        iterator(arr[completed], function (err) {
            if (err) {
                callback(err);
                callback = function () {};
            }
            else {
                completed += 1;
                if (completed === arr.length) {
                    callback(null);
                }
                else {
                    iterate();
                }
            }
        });
    };
    iterate();
};

您将看到您已经开始实施的相同递归模式。另一种选择是并行启动它们并在它们返回时跟踪它们。一旦列表中的第一项以未访问状态返回,您就可以退出。注意:以下代码未经测试...

var urls = [a,b,c,d],
    unvisitedUrls = [],
    count = urls.length,
    done = false;

var checkUrl = function(d) {
  var url = d;

  return function(visitItems) {
    if (done) return;
    count--;

    if (visitItems && visitItems.length > 0) {
        unvisitedUrls.push(url);
    }
    else {
        urls.splice(urls.indexOf(url));  // remove the visited url
    }

    if(unvisitedUrls.indexOf(urls[0]) > -1 || count === 0) {
        done = true;
        // done checking urls, urls[0] is the winner
    }

  }
}


urls.forEach(function(d) { chrome.history.getVisits(d, checkUrl(d)); });

如果您的列表有数百万个项目,那么您可以分批迭代它们,而不是一次全部迭代。这是一个使用https://github.com/caolan/asyncasync上的库的示例。

var checkUrl = function(url, cb) {

  chrome.history.getVisits(url, function(itemVisits) {   

    if (done) return cb();
    count--;

    if (visitItems && visitItems.length > 0) {
        unvisitedUrls.push(url);
    }
    else {
        urls.splice(urls.indexOf(url));  // remove the visited url
    }

    if(unvisitedUrls.indexOf(urls[0]) > -1 || count === 0) {
        done = true;
        // done checking urls, urls[0] is the winner
    }

    cb();
  }
};

async.forEachLimit(urls, 50, checkUrl, function(err) { doSomethingWithWinner(); });
于 2012-10-23T03:39:17.437 回答