Javascript 同步使用 getScript

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14783046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 17:54:19  来源:igfitidea点击:

Using getScript synchronously

javascriptjqueryajaxgetscript

提问by VashGH

I'm writing an engine that requires the use of getScript quite extensively. I've pushed it into its own function, for ease of use, but now I need to make sure that the function itself is synchronous. Unfortunately, I can't seem to make getScript wait until the script it loads is actually finished loading before proceeding. I've even tried setting jQuery's ajax asynch property to false before making the call. I'm thinking of using jQuery's when/done protocol, but I can't seem to wrap my head around the logic of placing it inside a function and making the function itself synchronous. Any help would be very much appreciated!

我正在编写一个需要广泛使用 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
});
}

Loop code (by request):

循环代码(按要求):

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

采纳答案by Felix Kling

As I said, it's relatively easy to chain Ajax calls with promise objects. Now, it don't see whythe scripts have to be loaded one after the other, but you will have a reason for it.

正如我所说,将 Ajax 调用与 promise 对象链接起来相对容易。现在,它不明白为什么必须一个接一个地加载脚本,但是您将有理由这样做。

First though I would get rid of the switchstatement if you are only calling the same function with different arguments. E.g. you can put all the script URLs in a map:

首先,switch如果你只用不同的参数调用同一个函数,我会去掉这个语句。例如,您可以将所有脚本 URL 放在地图中:

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

You can chain promises by simply returning another promise from a callback passed to .then[docs]. All you need to do is start with a promise or deferred object:

您可以通过简单地从传递给.then[docs]的回调中返回另一个承诺来链接承诺。你需要做的就是从一个 promise 或 deferred 对象开始:

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

In loadScript, you simply return the promise returned from $.getScriptor the one returned by .done:

在 中loadScript,您只需返回从$.getScript或返回的承诺.done

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

The scripts will all be called in the order the are access in the loop. Note that if divlistis an array, you really should use normal forloop instead of a for...inloop.

脚本将按照循环中访问的顺序调用。请注意,如果divlist是一个数组,您确实应该使用普通for循环而不是for...in循环。

回答by Sachseb1

This worked for me, and may help you.

这对我有用,可能对你有帮助。

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

Basically, I just bypassed the shorthand notation and added in the async: false

基本上,我只是绕过了速记符号并添加到 async: false

回答by Felix Kling

Do you know that $.getScriptacceptsa callback function that is called synchronously after the script is loaded?

你知道在脚本加载后$.getScript接受同步调用的回调函数吗?

Example:

例子:

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

I have 2 more solutions: a pure jsone and one for multiple js load.

我还有 2 个解决方案:一个纯 js一个,一个用于多个 js load

回答by kupriyanenko

Try this way, create array with deferred objects and used $.when with "apply"

试试这种方式,使用延迟对象创建数组并使用 $.when 和“apply”

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

回答by yckart

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

回答by Erutan409

@Felix Kling's answer was a great start. However, I discovered that there was a slight issue with the overall attached .done()at the end of the .getScripts()returned result if I wanted to "functionalize" it. You need the last promise from the chained .getScript()iterations from within the loop. Here's the modified version of his solution (thank you, BTW).

@Felix Kling 的回答是一个很好的开始。但是,我发现如果我想“功能化”它.done().getScripts()返回结果末尾的整体附加有一个小问题。您需要.getScript()循环内链式迭代的最后一个承诺。这是他的解决方案的修改版本(谢谢,顺便说一句)。

Plugin:

插入:

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

You can ignore the fetched function (I implemented it to reduce potential redundant calls - which is why I hiHymaned .getScript()) and see where the variable lastis set inside the .getScripts()method. It defaults to a resolved deferred object, so that if the urls array is empty, it's passed to the returned result to attach the outer .done()call to. Otherwise, it will inevitably be assigned the last promise object from the chained .getScript()calls and thus will ensure everything will remain synchronous from outside the function.

您可以忽略 fetched 函数(我实现它是为了减少潜在的冗余调用 - 这就是我劫持的原因.getScript())并查看变量last.getScripts()方法中的设置位置。它默认为已解析的延迟对象,因此如果 urls 数组为空,则将其传递给返回的结果以附加外部.done()调用。否则,它将不可避免地从链式.getScript()调用中分配最后一个 promise 对象,从而确保所有内容从函数外部保持同步。

Returning the initially created deferred object will not work if you resolve it before returning it back to the invoker (which is what you're supposed to do per jQuery's official documentation).

如果您在将其返回给调用者之前解决了它,则返回最初创建的延迟对象将不起作用(根据jQuery 的官方文档,您应该这样做)。

Example:

例子:

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.');
        });
}

回答by Stuart Lawrence

this is what I do

这就是我所做的

function loadJsFile(filename) {
    $.ajaxSetup({
        cache: true
    });

    var dloadJs = new $.Deferred();
    $.when(dloadJs).done(function () {
        $.ajaxSetup({
            cache: false
        });
    });

    dloadJs.resolve(
         $.getScript(filename, function () { })
    );
}

回答by Roger C

Just create a script node, set its src property to the JS you want to load then append it to the head:

只需创建一个脚本节点,将其 src 属性设置为要加载的 JS,然后将其附加到头部:

var myScript = document.createElement('script');
myScript.src = "thesource.js";
document.head.appendChild(myScript);