web-dev-qa-db-ja.com

getScriptを同期的に使用する

私は、getScriptをかなり広範囲に使用する必要があるエンジンを書いています。使いやすくするために、独自の関数にプッシュしましたが、関数自体が同期していることを確認する必要があります。残念ながら、先に進む前に、ロードするスクリプトが実際にロードを完了するまでgetScriptを待機させることができないようです。呼び出しを行う前に、jQueryのajax asynchプロパティをfalseに設定してみました。 jQueryのwhen/doneプロトコルを使用することを考えていますが、関数内に配置して関数自体を同期させるロジックに頭を悩ませているようには見えません。どんな助けでも大歓迎です!

function loadScript(script){
//Unrelated stuff here!!!
$.when(
$.getScript(script,function(){
    //Unrelated stuff here!!!
})).done(function(){
    //Wait until done, then finish function
});
}

ループコード(リクエストによる):

for (var i in divlist){
        switch($("#"+divlist[i]).css({"background-color"})){
            case #FFF:
            loadScript(scriptlist[0],divlist[i]);
            break;
        case #000:
            loadScript(scriptlist[2],divlist[i]);
            break;
        case #333:
            loadScript(scriptlist[3],divlist[i]);
            break;
        case #777:
            loadScript(scriptlist[4],divlist[i]);
            break;
    }
}
22
VashGH

すでに述べたように、Ajax呼び出しをpromiseオブジェクトにチェーンするのは比較的簡単です。 なぜスクリプトが次々にロードされなければならないかわかりませんが、それには理由があります。

ただし、同じ関数を異なる引数で呼び出すだけの場合は、switchステートメントを削除します。例えば。すべてのスクリプトURLをマップに配置できます。

var scripts = {
    '#FFF': '...',
    '#000': '...'
    // etc.
};

.thenに渡されるコールバックから別のpromiseを返すだけで、promiseをチェーンできます[ドキュメント] 。あなたがする必要があるのは、promiseまたは据え置きオブジェクトから始めることです:

var deferred = new $.Deferred();
var promise = deferred.promise();

for (var i in divlist) {
    // we need an immediately invoked function expression to capture
    // the current value of the iteration 
    (function($element) {
        // chaining the promises, 
        // by assigning the new promise to the variable
        // and returning a promise from the callback
        promise = promise.then(function() {
            return loadScript(
                scripts[$element.css("background-color")], 
                $element
            );
        });
    }($('#' + divlist[i])));
}

promise.done(function() {
    // optional: Do something after all scripts have been loaded
});

// Resolve the deferred object and trigger the callbacks
deferred.resolve();

loadScriptでは、$.getScriptから返されたpromiseまたは.doneから返されたpromiseを返すだけです。

function loadScript(script_url, $element){
    // Unrelated stuff here!!!

    return $.getScript(script_url).done(function(){
        //  Unrelated stuff here
        // do something with $element after the script loaded.
    });
}

スクリプトはすべて、ループ内のアクセス順に呼び出されます。 divlistが配列の場合、for...inループの代わりに通常のforループを実際に使用する必要があることに注意してください。

11
Felix Kling

これは私にとってはうまくいき、あなたを助けるかもしれません。

$.ajax({
    async: false,
    url: "jui/js/jquery-ui-1.8.20.min.js",
    dataType: "script"
});

基本的に、私は省略表記をバイパスしてasync: falseに追加しました

28
Sachseb1

知っていますか $.getScriptaccepts スクリプトが読み込まれた後に同期的に呼び出されるコールバック関数?

例:

$.getScript(url,function(){
//do after loading script
});

さらに2つの解決策があります。a pure js one and one for multiple js load です。

5
user669677

この方法を試して、遅延オブジェクトで配列を作成し、「適用」で$ .whenを使用します

var scripts = [
    'src/script1.js',
    'src/script2.js'
];

var queue = scripts.map(function(script) {
    return $.getScript(script);
});

$.when.apply(null, queue).done(function() {
    // Wait until done, then finish function
});
3
kupriyanenko
var getScript = function(url) {
    var s = document.createElement('script');
    s.async = true;
    s.src = url;
    var to = document.getElementsByTagName('script')[0];
    to.parentNode.insertBefore(s, to);
};
0
yckart

@Felix Klingの回答は素晴らしいスタートでした。ただし、.done()で返された結果の最後にある.getScripts()の全体に少し問題があることがわかりました。ループ内からの連鎖.getScript()反復の最後のプロミスが必要です。これが彼のソリューションの修正バージョンです(ありがとう、BTW)。

プラグイン:

(function ($) {
    var fetched = new function () {
            this.scripts = [];
            this.set = [];

            this.exists = function (url) {
                var exists = false;

                $.each(this.set, function (index, value) {
                    if ((url || '') === value) {
                        exists = true;

                        return false;
                    }
                });

                return exists;
            };

            this.buildScriptList = function () {
                var that = this;

                that.set = [];

                $('script').each(function () {
                    var src = $(this).attr('src') || false;

                    if (src) {
                        that.set.Push(src);
                    }
                });

                $.merge(this.set, this.scripts);

                return this;
            };
        },
        getScript = $.getScript;

    $.getScript = function () {
        var url = arguments[0] || '';

        if (fetched.buildScriptList().exists(url)) {
            return $.Deferred().resolve();
        }

        return getScript
            .apply($, arguments)
            .done(function () {
                fetched.scripts.Push(url);
            });
    };

    $.extend({
        getScripts: function (urls, cache) {
            if (typeof urls === 'undefined') {
                throw new Error('Invalid URL(s) given.');
            }

            var deferred = $.Deferred(),
                promise = deferred.promise(),
                last = $.Deferred().resolve();

            if (!$.isArray(urls)) {
                urls = [urls];
            }

            $.each(urls, function (index) {
                promise = promise.then(function () {
                    last = $.getScript(urls[index]);

                    return last;
                });
            });

            if (Boolean(cache || false) && !Boolean($.ajaxSetup().cache || false)) {
                $.ajaxSetup({cache: true});

                promise.done(function () {
                    $.ajaxSetup({cache: false});
                });
            }

            deferred.resolve();

            return last;
        }
    });
})($);

フェッチした関数を無視して(潜在的な冗長呼び出しを減らすために実装しました-これが.getScript()を乗っ取った理由です)、変数last.getScripts()メソッド内で設定されている場所を確認できます。デフォルトでは解決された遅延オブジェクトなので、urls配列が空の場合、返された結果に渡されて、外側の.done()呼び出しがアタッチされます。それ以外の場合は、必然的にチェーンされた.getScript()呼び出しからの最後のpromiseオブジェクトが割り当てられるため、すべてが関数の外部から同期されたままになります。

最初に作成された据え置きオブジェクトを呼び出し元に戻す前に解決した場合、オブジェクトを戻しても機能しません(これは jQueryの公式ドキュメント に従って行うことになっています)。

例:

function loadStuff(data) {
    var version = {
        'accounting': '1.2.3',
        'vue': '1.2.3',
        'vueChart': '1.2.3'
    };

    $.getScripts([
        'https://cdnjs.cloudflare.com/ajax/libs/accounting.js/' + version.accounting + '/accounting.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/vue/' + version.vue + '/vue.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/vue-chartjs/' + version.vueChart + '/vue-chartjs.min.js'
    ], true)
        .done(function () {
            // do stuff
        })
        .fail(function () {
            throw new Error('There was a problem loading dependencies.');
        });
}
0
Erutan409