1

私の Chrome 拡張機能には一連の URL があり、まだアクセスしていない最初の URL を見つけたいと考えています。API は非同期であるためchrome.history、私の最初の本能は、このようなグロテスクな再帰的反復を行うことです...

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

実装を開始したのと同じ再帰パターンが表示されます。もう1つのオプションは、すべてを並行して開始し、戻ってきたときに追跡することです。リストの最初のアイテムが未訪問として戻ってきたらすぐに終了できます。注:次のコードはテストされていません...

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 に答える