3

次のような状況があります。サーバーからファイル A をロードしたいのですが、サーバーはファイル A1、A2、A3、... An をロードしようとし、各ファイル A[1-n] は他のファイルをロードし、これは続行できます。しかし、それには終わりがあります。(async: false を使用してブラウザーがハングしないように) 遅延オブジェクトを使用してセットアップしたいのですが、ファイルの読み込みと解析の再帰により、オブジェクトのセットアップ方法がわかりません。さらに、レベル (l-1) を続行する前に、最高の再帰深度レベル (l) を終了する必要があります。再帰がない場合、このコードは機能しますが、再帰的な場合はわかりません。

var loadFile = function (index, url, scope, callback) {
    $.ajax ({url: url, type: 'GET', dataType: 'text'})
    .done (function (responseText) {
        // store response in array
        scope.requests[index] = responseText;
    })
    .fail (function (xhr, status, error) {
        scope.requests[index] = 'error';
    })
    .always (function (responseText) {
        // loop through entire response array from the beginning
        for (var j = 0; j < scope.requests.length; j++)
        {
            if (scope.requests[j] === 'unprocessed') return;
            else if (scope.requests[j] === 'error')
                scope.requests[j] = 'done';
            else if (scope.requests[j] !== 'done')
            {
                parseFile (scope.requests[j], scope, callback);
                scope.requests[j] = 'done';
            }
        }

        // if all are done then reset the array and run the callback
        delete scope.requests;

        if (callback) callback();
    });
}

var parseFile = function (responseText, scope, callback) {
    var lines = responseText.split("\n");

    for (var l = 0; l < lines.length; l++) {
        var line = lines[l];
        line = line.replace (/^\s+/, ''); // remove leading white space
        if (line.charAt(0) === '1') // file reference
        {
            var attrs = line.split (/\s+/);

            // the file will exist in any of the paths in pathList
            for (var i = 0; i < scope.pathList.length; i++) {
                scope.requests.push ('unprocessed');
                loadFile (++index, scope.pathList[i] + attrs[14], scope, callback);
            }
        }
    }
}

var index = 0;
var this.requests = [];
this.requests.push ('unprocessed');
loadFile (index, fileAi, this, callback);
4

2 に答える 2

2

基本的な考え方は次のとおりです。

  1. ajax現在のレベルの各ファイルのリクエストを送信します。
  2. レベル全体が完了したら、すべての応答を解析します。
  3. それ以上ある場合は、再帰します。そうでない場合は、コールバックを呼び出します。

要求しているファイルの一部が存在しないため (これは予想されることです)、独自の Deferred を作成する必要があります。次に、失敗を効果的に無視して、ajaxdoneとコールバックからそれらを解決できます。fail

また、ご要望に応じてキャッシュ オブジェクトを追加しました。オブジェクトは URL を promise にマップします。すでに解決されている promise にコールバックをアタッチすると、コールバックは同じ引数doneですぐに呼び出されます。response応答の代わりに要求をキャッシュしているため、最初の要求をキャッシュに入れるために終了する必要がないため、これは物事をキャッシュするための優れた方法です。したがって、最初のリクエストが完了する前に同じファイルを 4 回リクエストしても、ajax呼び出しは 1 回だけです。

注: function を追加したのでgetFile、コメントからのスコープ/クロージャの問題は (各dfd変数が関数スコープ内にあるため) 問題ではなくなり、コードの混乱が少し軽減されたと思います。問題は、非常に一般的なループスコープの 問題でした。

コード:

// load all files, starting with startUrl.  
// call callback when done.
var loadAll = function(startUrl, callback) {
    var pathList = []; // assuming this has some base urls in it.
    var dfds = []; // dfds for the current level.
    var urls = [startUrl]; // urls for current level.
    var responses = []; // responses for current level.
    var cache = {}; // object to map urls to promises.

    // given the responseText, add any referenced urls to the urls array
    var parseFile = function (responseText) {
        var lines = responseText.split("\n");

        for (var l = 0; l < lines.length; l++) {
            var line = lines[l];
            line = line.replace (/^\s+/, ''); // remove leading white space
            if (line.charAt(0) === '1') // file reference
            {
                var attrs = line.split (/\s+/);

                // the file will exist in any of the paths in pathList
                for (var i = 0; i < pathList.length; i++) {
                    // add new path to urls array
                    urls.push (pathList[i] + attrs[14]);
                }
            }
        }
    };

    // load one file.
    // check cache for existing promise for the url.
    var getFile = function(url) {
        var dfd;

        if(cache.hasOwnProperty(url)){
            // use cached promise.
            // if it is already resolved, any callback attached will be called immediately.
            dfd = cache[url];
            dfds.push(cache[url]);
        } else {
            dfd = $.Deferred();
            $.ajax ({url: url, type: 'GET', dataType: 'text'}).done(function(response){
                // resolve and pass response.
                dfd.resolve(response);
            }).fail(function(){
                // resolve and pass null, so this error is ignored.
                dfd.resolve(null);
            });
            dfds.push(dfd.promise());
            cache[url] = dfd.promise();
        }

        // when the request is done, add response to array.
        dfd.done(function(response) {
            if(response){
                // add to responses array.
                // might want to check if the same response is already in the array.
                responses.push(response);
            }
        });
    };

    // request each file in the urls array.
    // recurse when all requests done, or call callback.
    var loadLevel = function () {
        dfds = [];
        responses = [];

        for (var l = 0; l < urls.length; l++) {
            getFile(urls[l]);
        }

        $.when.apply($, dfds).done(function(){
            // entire level is done loading now.
            // each done function above has been called already, 
            // so responses array is full.
            urls = [];

            // parse all the responses for this level.
            // this will refill urls array.
            for (var i = 0; i < responses.length; i++) {
                parseFile(responses[i]);
            }

            if(urls.length === 0) {
                // done
                callback();
            } else {
                // load next level
                loadLevel();
            }
        });
    };

    // load first level
    loadLevel();
};
于 2013-04-01T14:37:32.197 に答える
0

書かれているように、コードは再帰が巻き戻される前に常にブランチ全体を完了しようとするため、つまり、次の構造が与えられているため、コードをそのまま構造化して「レベルごと」のブロックを達成できるとは思いません。

     1
    / \
   2   6
  /|\  |
 3 4 5 7

表示されている番号順にノードをたどります[1] [2 6] [3 4 5 7](または、おそらく意味がありました[3 4 5 7] [2 6] [1]か?)

完全な解決策を提供することはできません。役立つと思われるいくつかのヒントを提供します。

  1. 各レベルの配列を作成する必要があります。これには、そのレベルで要求された各ファイルの遅延オブジェクトが含まれています。

  2. jqXHRその場合は再帰も行うため、オブジェクトを使用することはできません。そのため、自分で.fail別のオブジェクトを作成し、ハンドラー内$.Deferred()でそれを作成する必要があります。.resolve.always

  3. のすべての要素が完了した$.when.apply($, myArray).done(...)ときにのみ発生するコールバックをトリガーするために使用します。myArray

于 2013-03-28T13:07:19.773 に答える