简体   繁体   English

使用 Promises 用 Ja​​vaScript 加载 jQuery

[英]Load jQuery with JavaScript using Promises

A previous question revealed how to load in jQuery using native JavaScript. 上一个问题揭示了如何使用原生 JavaScript 加载 jQuery。 I've successfully used the callback code from the answer there, replicated here:我已经成功地使用了答案中的回调代码,复制在这里:

// Anonymous "self-invoking" function
(function() {
    // Load the script
    var script = document.createElement("SCRIPT");
    script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
    script.type = 'text/javascript';
    document.getElementsByTagName("head")[0].appendChild(script);

    // Poll for jQuery to come into existance
    var checkReady = function(callback) {
        if (window.jQuery) {
            callback(jQuery);
        }
        else {
            window.setTimeout(function() { checkReady(callback); }, 100);
        }
    };

    // Start polling...
    checkReady(function($) {
        // Use $ here...
    });
})();

How can I accomplish the same thing using native JavaScript Promises?如何使用原生 JavaScript Promises完成同样的事情

The reason I ask is because I suddenly need to chain off of the earlier callback, and it's a friggin' mess.我问的原因是因为我突然需要链接先前的回调,这真是一团糟。 I'm hoping Promises are a better way, and I have no real interest in using a loader framework.我希望 Promises 是一种更好的方式,而且我对使用加载器框架没有真正的兴趣。

Here's what I've got so far, but the Promise always ends up rejected:到目前为止,这是我所得到的,但 Promise 总是被拒绝:

// This code doesn't work quite right.
// Poll for jQuery to come into existance using a Promise
var jQueryReady = new Promise(
    function(resolve, reject) {

      // Load jQuery
      var script = document.createElement('SCRIPT');
      script.type = 'text/javascript';
      script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js';
      document.getElementsByTagName('head')[0].appendChild(script);

      if (window.jQuery) {
        resolve("YAY");
      } else {
        reject("UGH");
      }
    });

jQueryReady.then(
  function(success) {
    console.log(success);
  },
  function(error) {
    console.error("Really helpful error:", error);
  });

(I'm sorry in advance for my complete ignorance.) (我提前为我的完全无知感到抱歉。)

Here's a version that makes a simple loadScript() function that returns a promise and then provides a wrapper around it that detects whether jQuery is already loaded:这是一个生成一个简单的loadScript()函数的版本,该函数返回一个 promise,然后提供一个包装器来检测 jQuery 是否已经加载:

function loadScript(url) {
    return new Promise(function(resolve, reject) {
        var script = document.createElement("script");
        script.onload = resolve;
        script.onerror = reject;
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    });
}

function loadjQuery() {
    if (window.jQuery) {
        // already loaded and ready to go
        return Promise.resolve();
    } else {
        return loadScript('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js');
    }
}


// Usage:
loadjQuery().then(function() {
    // code here that uses jQuery
}, function() {
    // error loading jQuery
});

Notes on your code:关于您的代码的说明:

  1. In your first code block, setting a single timer and assuming that the script will be loaded when that timer fires is like playing roulette.在您的第一个代码块中,设置单个计时器并假设该计时器触发时将加载脚本就像玩轮盘赌一样。 It might work most of the time, but it is not a purely reliable method of doing things.它可能在大部分时间都有效,但它并不是一种纯粹可靠的做事方法。 In addition, to be safe, you have to set the timer to a longer period of time than is usually necessary.此外,为了安全起见,您必须将计时器设置为比通常需要的时间更长的时间。 Instead, you should trigger based on the onload callback of the script.相反,您应该基于脚本的onload回调来触发。 Then you will know exactly when the script is ready with 100% reliability.然后,您将确切知道脚本何时准备就绪,并且具有 100% 的可靠性。

  2. In your second code block, your promise version successfully handles the case where jQuery is already loaded, but then rejects() when jQuery must be custom loaded.在您的第二个代码块中,您的 Promise 版本成功处理了 jQuery 已加载的情况,但在必须自定义加载 jQuery 时rejects() As you can see from my example, you need to resolve() when the newly loaded script tag has finished loading for your promise to work as desired.正如您从我的示例中看到的,您需要在新加载的脚本标记完成加载后使用resolve()才能让您的承诺按预期工作。

When you have multiple scripts with dependencies, I've found a promise sequence like this works best:当您有多个具有依赖项的脚本时,我发现像这样的 Promise 序列效果最好:

let p1 = loadScript('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js')
let p2 = loadScript('scripts/moment.min.js')
            
Promise.allSettled([p1, p2])
.then(function() {
    return loadScript('scripts/myScript.js')
}).then(function() {
    console.log('All Scripts Loaded!')
    initMyScript()
})
.catch(error => {
    console.error(error.message)
})


function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script')
    script.src = src
    script.onload = resolve
    script.onerror = reject
    document.head.append(script)
  })
}

Scripts inserted into the page in this way are executed asynchronously.以这种方式插入页面的脚本是异步执行的。 (See"Dynamically importing scripts" on MDN.) As a result, window.jQuery will always be undefined . (请参阅 MDN 上的“动态导入脚本” 。)因此, window.jQuery将始终是undefined

Try attaching an onload handler to the script element such that resolution of the Promise is only performed once the script has been executed.尝试将onload处理程序附加到脚本元素,这样 Promise 的解析只会在脚本执行后执行。

For example (before setting script.src ):例如(在设置script.src之前):

script.onload = function () {
    if (window.jQuery) {
        resolve("YAY");
    } else {
        reject("UGH");
    }
};

As usual, this method is not compatible with all browsers.像往常一样,此方法并不与所有浏览器兼容。 Check this post out for ways to accommodate them.查看这篇文章以了解容纳它们的方法。

The problem is that this is an asyncrhonous non-blocking way of loading javascript.问题是这是一种异步非阻塞加载 javascript 的方式。 You'll have to wait for the browser to download the script.您必须等待浏览器下载脚本。

This is a possible solution:这是一个可能的解决方案:

var jQueryReady = new Promise(
  function(resolve, reject) {

    // Load jQuery
    var script = document.createElement('SCRIPT');
    script.type = 'text/javascript';
    script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js';
    document.getElementsByTagName('head')[0].appendChild(script);

    // You should also have a timeout in case your script never loads
    var timeoutHandler = setTimeout(10000, function() {
      if (checkIntervalHandler) clearInterval(checkIntervalHandler);
      reject("UGH");
    });

    var checkIntervalHandler = setInterval(500, function() {
      if (window.jQuery) {
        if(timeoutHandler) clearTimeout(timeoutHandler);
        resolve("YAY");
      }
    });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM